读书笔记

日期:2019-09-07编辑作者:编程应用

RemoteViews是一种远程View,能够在别的进程中显得,为了能够更新它的分界面,RemoteViews提供了一组基础操成效于跨进度更新它的分界面。本章会介绍RemoteViews在通知栏和桌面小部件上的使用,分析RemoveViews的在那之中机制,最终分析RemoteViews的含义并交由三个利用RemoteViews来跨进程更新分界面包车型大巴亲自过问。

RemoteView 在Android中的使用境况有两种: 通告栏和桌面小部件。《Android开拓格局》第5章入眼解析了经过RemoteViews完成长途更新(文告栏和小部件)分界面。

RemoteViews首要用以通告栏和桌面小部件的费用。文告栏首要通过NotificationManager的notify方法来落到实处;桌面小部件则是经过AppWidgetProvider来贯彻的,AppWidgetProvider本质上是四个播放。因为RemoteViews运营在其它进度(SystemService进程),所以不能够直接更新分界面。

1 RemoteViews应用

通知栏
Notification相关类,做了立异,例子很轻巧,最新的API代码能够参照官方教程
至于RemoteViews更新分界面包车型客车代码:

RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.layout_notification);
remoteViews.setTextViewText(R.id.msg, "chapter_5: " + sId);
remoteViews.setImageViewResource(R.id.icon, R.drawable.icon1);
PendingIntent openActivity2PendingIntent = PendingIntent.getActivity(this,
0, new Intent(this, DemoActivity_2.class), PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.open_activity2, openActivity2PendingIntent);

桌面小部件
桌面小部件通过AppWidgetProvider来实现的,它的本来面目是一个广播。AppWidgetProvider提供了多少个重要的点子, onUpdate, onEnable, onDisable, onDeleted 以及 onReceive。 个中onReceive会依照广播的Action响应, 然后再调用别的格局。
与广播类似, 当桌面小部件接收到客商的相互音讯,则会由此onReceive传递, 客户通过重写onReceive方法,并认清intent.getAction()是或不是须要做相应的拍卖。 假如是革新分界面,就要求通过RemoveView完结:

@Override
    public void onReceive(final Context context, Intent intent) {
        super.onReceive(context, intent);
        Log.i(TAG, "onReceive : action = " + intent.getAction());

        // 这里判断是自己的action,做自己的事情,比如小工具被点击了要干啥,这里是做一个动画效果
        if (intent.getAction().equals(CLICK_ACTION)) {
            Toast.makeText(context, "clicked it", Toast.LENGTH_SHORT).show();

            new Thread(new Runnable() {
                @Override
                public void run() {
                    Bitmap srcbBitmap = BitmapFactory.decodeResource(
                            context.getResources(), R.drawable.icon1);
                    AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
                    for (int i = 0; i < 37; i++) {
                        float degree = (i * 10) % 360;
                        RemoteViews remoteViews = new RemoteViews(context
                                .getPackageName(), R.layout.widget);
                        remoteViews.setImageViewBitmap(R.id.imageView1,
                                rotateBitmap(context, srcbBitmap, degree));
                        Intent intentClick = new Intent();
                        intentClick.setAction(CLICK_ACTION);
                        PendingIntent pendingIntent = PendingIntent
                                .getBroadcast(context, 0, intentClick, 0);
                        remoteViews.setOnClickPendingIntent(R.id.imageView1, pendingIntent);
                        appWidgetManager.updateAppWidget(new ComponentName(
                                context, MyAppWidgetProvider.class),remoteViews);
                        SystemClock.sleep(30);
                    }

                }
            }).start();
        }
    }
5.1.1 RemoteViews在布告栏上的利用
 Notification notification = new Notification(); notification.icon = R.mipmap.ic_launcher; notification.tickerText = "hello notification"; notification.when = System.currentTimeMillis(); notification.flags = Notification.FLAG_AUTO_CANCEL; Intent intent = new Intent(this, RemoteViewsActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.layout_notification);//RemoveViews所加载的布局文件 remoteViews.setTextViewText(R.id.tv, "这是一个Test");//设置文本内容 remoteViews.setTextColor(R.id.tv, Color.parseColor("#abcdef"));//设置文本颜色 remoteViews.setImageViewResource(R.id.iv, R.mipmap.ic_launcher);//设置图片 PendingIntent openActivity2Pending = PendingIntent.getActivity (this, 0, new Intent(this, MainActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);//设置RemoveViews点击后启动界面 remoteViews.setOnClickPendingIntent(R.id.tv, openActivity2Pending); notification.contentView = remoteViews; notification.contentIntent = pendingIntent; NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); manager.notify(2, notification);

2 PendingIntent

  • 何以选用PendingIntent
    PendingIntent表示一种处于Pending状态的意向, 就是一种特定,等待,就要产生的意思。为何要使用PendingIntent呢? 作者在stackoverflow中看看多少个更加好掌握的讲授:

A PendingIntent is a token that you give to a foreign application (e.g. NotificationManager, AlarmManager, HomeScreen AppWidgetManager , or other 3rd party applications), which allows the foreign application to use your application's permissions to execute a predefined piece of code.

也正是说, 其余进度如若想要在你的app上做一些事务,即便依旧传三个Intent,他们是绝非施行权限的, 你必需给她们传PendingIntent, 他们才足以实施,因为PendingIntent包罗了试行的权位。
因而, 大家看来上边通告的这段代码, PendingIntent的采纳场景,正是给Remoteview设定八个点击的作为,张开德姆oActivity_2这个activity。

--情势参数
PendingIntent协理3种特定意图: getActivity(), getService(),get布罗兹cast(), 分别是开辟activity, service和broadcast。

getActivity(Context context, int requestCode, Intent intent, int flags)

requestCode: 表示发送方的需要码(多数意况为0)
flags: 表示PendingIntent类型的标记位,当多少个PendingIntent相称,通过这些标识位来调整是还是不是代表或许重新。

举例Intent与requestCode一样, 即表示PendingIntent相称。Intent相称依赖于ComponentName 和 intent-filter

flags 表示的含义:
FLAG_ONE_SHOT: 表示近些日子的PendingIntent只可以被利用三次, 然后就能被电动Cancel掉, 后续就算还可能有分外的,send方法就能失效。
FLAG_NO_CREATE: 不能单独使用,在付出中很少见。(不会积极创立)
FLAG_CANCEL_CURAV4RENT: 当前描述的PendingIntent假如存在, 那么它们都会被cancel, 然后系统会成立多个新的。
FLAG_UPDATE_CUWranglerRENT: 当前一经存在,那么都会被更新, 即Intent中的Extras会被替换到最新的。

5.1.2 RemoveViews在桌面小部件上的利用
  1. 概念好小部件分界面在res/layout下新建三个xml文件,命名称叫widget.xml,名称和内容能够自定义。
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:andro android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <ImageView android: android:layout_width="360dp" android:layout_height="360dp" android:layout_gravity="center" /></LinearLayout>
  1. 概念小部件的布置音信在res/xml/下新建appwidget_provider_info.xml,名称任意。
<?xml version="1.0" encoding="utf-8"?><appwidget-provider xmlns:andro android:initialLayout="@layout/widget" android:minHeight="360dp" android:minWidth="360dp" android:updatePeriodMillis="864000"/>
  1. 概念小部件的贯彻类这一个类需求承袭AppWidgetProvider;我们这里完成二个简短的widget,点击它后,3张图片随机切换彰显。
public class ImgAppWidgetProvider extends AppWidgetProvider { public static final String TAG = "ImgAppWidgetProvider"; public static final String CLICK_ACTION = "cn.hudp.androiddevartnote.action.click"; private static int index; @Override public void onReceive(Context context, Intent intent) { super.onReceive(context, intent); if (intent.getAction().equals(CLICK_ACTION)) { RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget); AppWidgetManager appWidgetManager = AppWidgetManager.getInstance; updateView(context, remoteViews, appWidgetManager); } } @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { super.onUpdate(context, appWidgetManager, appWidgetIds); RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget); updateView(context, remoteViews, appWidgetManager); } // 由于onReceive 和 onUpdate中部分代码相同 则抽成一个公用方法 public void updateView(Context context, RemoteViews remoteViews, AppWidgetManager appWidgetManager) { index =  (Math.random; if (index == 1) { remoteViews.setImageViewResource(R.id.iv, R.mipmap.haimei1); } else if (index == 2) { remoteViews.setImageViewResource(R.id.iv, R.mipmap.haimei2); } else { remoteViews.setImageViewResource(R.id.iv, R.mipmap.haimei3); } Intent clickIntent = new Intent(); clickIntent.setAction(CLICK_ACTION); PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, clickIntent, 0); remoteViews.setOnClickPendingIntent(R.id.iv, pendingIntent); appWidgetManager.updateAppWidget(new ComponentName(context, ImgAppWidgetProvider.class), remoteViews); }}
  1. 在AndroidManifest.xml中声称小部件因为桌面小部件的本质是一个播放组件,由此绝对要登记。
<receiver android:name=".RemoveViews_5.ImgAppWidgetProvider"> <meta-data android:name="android.appwidget.provider" android:resource="@xml/appwidget_provider_info"> </meta-data> <intent-filter> <action android:name="cn.hudp.androiddevartnote.action.click" /> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> </intent-filter></receiver>

地方代码中有七个action,第三个是用以识别小部件的单击行为,而首个则是作为小部件的标记必得存在的;假设不加那么些receiver就不是三个桌面小部件何况也无法展现在四弟大的小部件中。

  1. 广播到来的时候,AppWidgetProvider会自动依照广播的Action通过onReceive方法来散发广播,也正是调用onEnable: 当该窗口小部件第一次增加到桌面时调用的办法,可增进多次但只在第4回调用。onUpdate: 小部件被加多时可能每一趟小部件更新时都会调用三遍该措施,小部件的换代机会是有updatePeriodMillis来钦点,每种周期小部件就能自动更新三回。onDeleted: 每删除一遍桌面小部件就调用二回。onDisabled: 当最后两个该类型的小部件被去除时调用该格局。onReceive: 那是广播的放置方法,用于分发具体育赛事件给别的方式。

RemoteView 内部机制

作者们早就在上边包车型大巴事例中看到了RemoteView重倘若为了在长距离界面中更新View而存在的, 下边大家看看它当中的体制:

  1. 能够看出贰个景观:RemoteView未有提供findViewById方法, 因而不能直接待上访谈里面的view成分,而务必经过一多元的set方法(比方setTextViewText(int viewId, CharSequence text))
  2. 上述的七个例子NotificationManager/AppWidgetmanager分别通过Binder和NotificationManagerService/AppWidgetService进度通信, 所以通知栏和桌面部件的分界面也是在SystemService中被加载, 进而构成了跨进度通讯的场景。
  3. RemoteView实现了Parcelable接口,会通过Binder传递到SystemServer进程。
  4. 假若View的操作通过Binder达成的话,view的不二等秘书技太多资金较高,并且大批量的IPC操作会影响功效。消除措施:Android系统提供了Action的概念,Action封装了具体的操作, 然后传输到远程SystemServer进度中。
  5. RemoteViews内部有至关心注重要的点子(apply,reapply)来达成Action的操作。
5.1.3 PendingIntent概述

PendingIntent表示一种处于pending(待定、等待、就要产生)状态的用意;PendingIntent通过send和cancel方法来发送和注销一定的待定Intent。PendingIntent协理三种待定意图:运转Activity、运行Service和发送广播。分别对应:

getActivity / getService / getBroadcast参数相同,都为:(Context context, int requestCode, Intent intent, int flags) 

在那之中第二个参数,requestCode代表PendingIntent发送方的诉求码,多少意况下为0就能够,requestCode会影响到flags的成效。PendingIntent的相当准绳是:纵然三个PendingIntent他们之中的Intent相同而且requestCode也完全一样,那么那八个PendingIntent就是一样的。那么哪些状态下Intent相同呢?Intent的非常法规是,如果七个Intent的ComponentName和intent-filter都无差距;那么这五个Intent也是一样的。

flags参数的意思:FLAG_ONE_SHOP 当前的PendingIntent只可以被使用二回,然后他就能够自行cancel,假如后续还应该有雷同的PendingIntent,那么它们的send方法就能够调用退步。FLAG_NO_CREATE 当前描述的PendingIntent不会主动成立,假若当前PendingIntent从前存在,那么getActivity、getService和get布罗兹cast方法会直接重临Null,即取得PendingIntent战败,无法独立使用,平时非常少用到。FLAG_CANCEL_CURRENT 当前描述的PendingIntent要是已经存在,那么它们都会被cancel,然后系统会成立二个新的PendingIntent。对于布告栏音信的话,这些被cancel的音讯单击后不大概开垦。FLAG_UPDATE_CURRENT 当前描述的PendingIntent倘使已经存在,那么它们都会被更新,即它们的Intent中的Extras会被沟通为新型的。

NotificationManager的notify方法分析

manager.notify(1,notification);
  1. 假诺notify方法的id是常量,那么不论PendingIntent是或不是同盟,前边的通报都会交替掉前边的打招呼。
  2. 比方notify的主意id每便都不雷同,那么当PendingIntent不一致盟的时候,不管在何种标识为下,那么些布告都不会互相搅扰。
  3. 设若PendingIntent处于相称阶段,分情状:
  4. 采用FLAG_ONE_SHOT标识位,那么继续布告中的PendingIntent会和率先条公告保持一致,包罗内部的Extras,单击任何一条通告后,其余公告均无法再打开;当全数文告被扫除后。
  5. 采用FLAG_CANCEL_CUEnclaveRENT标志位,独有新型的打招呼能够张开,在此以前弹出的具有文告均无法展开。
  6. 采用FLAG_UPDATE_CU昂CoraRENT标识位,那么以前弹出的PendingIntent会被更新,最终它们和最新的一条保存完全一致,包涵内部的Extras,并且这个公告都以能够展开的。
  1. RemoteViews的构造方法:public RemoteViews(String packageName,int layoutId),第二个参数表示最近应用的包名,第叁个参数表示待加载的布局文件。
  2. RemoveViews并不能支持有着View类型,协理以下:Layout:FrameLayout、LinearLayout、RelativeLayout、GridLayout。View:Button、ImageButton、ImageView、ProgressBar、TextView、ListView、GridView、ViewStub等(举个例子艾德itText是分裂目的在于RemoveViews中利用的,使用会抛极度)。
  3. RemoteView没有findViewById方法,因而不能够访谈里面包车型大巴View元素,而必得经过RemoteViews所提供的一层层set方法来产生,那是透过反射调用的。
  4. 布告栏和小组件分别由NotificationManager和AppWidgetManager管理,而NM和AWM通过Binder分别和SystemService进程中的NotificationManagerService以及AppWidgetService中加载的,而它们运转在系统的SystemService中,那就和我们经过构成了跨进度通信。
  5. 行事流程:首先RemoteViews会通过Binder传递到SystemService进度,因为RemoteViews落成了Parcelable接口,由此它可以跨过程传输,系统会基于RemoteViews的包名等音信获得该利用的能源;然后经过LayoutInflater去加载RemoteViews中的布局文件。接着系统会对View进行一多种分界面更新任务,那个职分正是此前我们经过set来交付的。set方法对View的换代并不会立时实施,会记录下来,等到RemoteViews被加载现在才会实践。
  6. 为了提升成效,系统尚未平素通过Binder去协理具有的View和View操作。而是提供贰个Action概念,Action同样完成Parcelable接口。系统第一将View操作封装到Action对象并将这么些目的跨进度传输到SystemService进度,接着SystemService进度试行Action对象的具体操作。远程进度经过RemoteViews的apply方法来进行View的换代操作,RemoteViews的apply方法会去遍历全数的Action对象并调用他们的apply方法。这样避免了定义大量的Binder接口,也制止了汪洋IPC操作。
  7. apply和reApply的区别在于:apply会加载布局并立异分界面,而reApply则只会更新分界面。
  8. 关于单击事件,RemoteViews中只接济发起PendingIntent,不接济onClickListener这种情势。setOnClickPendingIntent用于给一般的View设置单击事件,不能够给集结(ListView/StackView)中的View设置单击事件(成本大,系统禁止了这种方法)。要是要给ListView/StackView中的item设置单击事件,必得将setPendingIntentTemplate和setOnClickFillInIntent组合使用能力够。

RemoteViews最大的意义在于实惠的跨进度更新UI。

  • 当贰个使用必要立异另二个利用的某些分界面,大家得以挑选拔AIDL来落实,但借使更新相比较频仍,成效会有标题,相同的时间AIDL接口就可能变得很复杂。要是选用RemoteViews就从未那几个标题,但RemoteViews仅扶助部分常用的View,假诺分界面包车型大巴View都以RemoteViews所支撑的,那么就能够设想动用RemoteViews。
  • 使用RemoteViews加载其余App的布局文件与能源。
 final String pkg = "cn.hudp.remoteviews";//需要加载app的包名 Resources resources = null; try { resources = getPackageManager().getResourcesForApplication; } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } if (resources != null) { int layoutId = resources.getIdentifier("activity_main", "layout", pkg); //获取对于布局文件的id RemoteViews remoteViews = new RemoteViews(pkg, layoutId); View view = remoteViews.apply(this, llRemoteViews);//llRemoteViews是View所在的父容器 llRemoteViews.addView; }

apply 方法

RemoteView中的大旨方法apply,用来加载布局,更新分界面,下边是非同平日的代码:

public class RemoteViews implements Parcelable, Filter {
    ......
    public View apply(Context context, ViewGroup parent, OnClickHandler handler) {
        RemoteViews rvToApply = getRemoteViewsToApply(context);
        View result;
        LayoutInflater inflater =(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        ......
        //加载布局
        result = inflater.inflate(rvToApply.getLayoutId(), parent, false);

        rvToApply.performApply(result, parent, handler);

        return result;
    }
     ......
}

能够见见,上述代码通过LayoutInflater加载布局,然后在performApply()对具备mActions中的Action都实行apply()操作:
1, 通过findViewById找到相应的view;
2, 通过Reflect机制,实践View完成类里的办法;

RemoteView的意义

能够看看,RemoteView主假设提供了经过间View更新的一种高效便捷的建设方案, 首要行使在Notification 和 AppWidgetProvider中, 然而RemoteView并不援救全体的View:

布局:FrameLayout、LinearLayout、RelativeLayout、GridLayout

组件:Button、ImageButton、ImageView、TextView、ListView、GridView、ViewStub等

本文由今晚最快开奖现场直播发布于编程应用,转载请注明出处:读书笔记

关键词:

热修复热点框架简析,Android插件框架机制研商

笔者 百度任玉刚 插件化框架 途牛已上线使用帮衬代码能源文件以插件情势加入到host。 插件化的主干形式是将二个...

详细>>

阅读笔记,线程安全性

首先使代码正确运行,然后再提高代码速度。【正确编写并发程序的方法】 线程安全:当多个线程访问某个类(对象...

详细>>

Android开发艺术探索,Bitmap获取缩略图

前言 追思了下此前写的调用相机和相册的机能,准备把它们构成下,想起已经用Motorola在赢得大图时OOM的主题素材,...

详细>>

Bitmaps加载之内部存储器管理

除了前面说的Bitmap缓存之外,还有一些事情我们可以做来使用好GC和Bitmap的重用.对于不同的Android版本要做不同的处理...

详细>>