Android 理解RemoteViews

导语

什么是远程view呢?它和远程service一样,RemoteViews可以在其他进程中显示。我们可以跨进程更新它的界面。在Android中,主要有两种场景:通知栏和桌面小部件。

本章先简单介绍通知栏和桌面小部件应用,接着分析RemoteViews内部机制,最后分析RemoteViews的意义并给出一个实例。

主要内容

  • RemoteViews的应用
  • RemoteViews的内部机制
  • RemoteViews的意义

具体内容

RemoteViews的应用

通知栏主要是通过NotificationManager的notify方法实现。桌面小部件是通过APPWidgetProvider来实现。APPWidgetProvider本质是一个广播。RemoteViews运行在系统的SystemServer进程。

RemoteViews在通知栏的应用

我们用到自定义通知,首先要提供一个布局文件,然后通过RemoteViews来加载,可以自定义通知的样式。更新view时,通过RemoteViews提供的一系列方法。如果给一个控件加点击事件,要使用PendingIntent。

RemoteViews在桌面小部件的应用

AppWidgetProvider是实现桌面小部件的类,本质是一个BroadcastReceiver。开发步骤如下:

  1. 定义小部件界面。代码
  2. 定义小部件配置信息。代码
  3. 定义小部件实现类,继承AppWidgetProvider。代码
    上面的例子实现了一个简单地桌面小部件,在小部件上显示一张图片,点击后会旋转一周。
  4. 在AndroidManifest.mxl中声明小部件。
 receiver android:name=".MyAppWidgetProvider" >
     <meta-data
         android:name="android.appwidget.provider"
         android:resource="@xml/appwidget_provider_info" >
     </meta-data>
 
     <intent-filter>
         <action android:name="com.ryg.chapter_5.action.CLICK" />
         <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
     </intent-filter>
 </receiver>

第一个action用于识别小部件的单击,第二个action作为小部件的标识必须存在。

AppWidgetProvider除了onUpdate方法,还有一系列方法。这些方法会自动被onReceive方法调用。当广播到来以后,AppWidgetProvider会自动根据广播的action通过onReceive方法分发广播。

  • onEnable:该小部件第一次添加到桌面时调用,添加多次只在第一次调用。
  • onUpdate:小部件被添加或者每次小部件更新时调用,更新时机由updatePeriodMillis指定,每个周期小部件都会自动更新一次。
  • onDeleted:每删除一次桌面小部件都会调用一次。
  • onDisabled:最后一个该类型的桌面小部件被删除时调用。
  • onReceive:内置方法,用于分发具体事件给以上方法。
PendingIntent概述

PendingIntent表示一种处于待定的状态的intent。典型场景是RemoteViews添加单击事件。通过send和cancel方法来发送和取消待定intent。

PendingIntent支持三种待定意图:

  • static PendingIntent:getActivity(Context context, int requestCode, Intent intent, int flags)获得一个PendingIntent,效果相当于Context.startActivity(Intent)。
  • static PendingIntent:getService(Context context, int requestCode, Intent intent, int flags)获得一个PendingIntent,效果相当于Context.startService(Intent)。
  • static PendingIntent:getBroadcast(Context context, int requestCode, Intent intent, int flags)获得一个PendingIntent,效果相当于Context.sendBroadcast(Intent)。

其中requestCode多数情况下设为0即可,requestCode会影响flags的效果。

PendingIntent的匹配规则:
如果两个PendingIntent,它们内部的Intent相同且requestCode也相同,那这两个PendingIntent就是相同的。

Intent的匹配规则:
如果两个intent的ComponentName和intent-filter相同,那么这两个intent相同。Extras不参与匹配过程。

flags参数的含义:

  • FLAG_ONE_SHOT
    当前的PendingIntent只能被使用一次,然后就会被自动cancel,如果后续还有相同的PendingIntent,它们的send方法会调用失败。对于通知栏来说,同类的通知只能使用一次,后续的通知将无法打开。
  • FLAG_NO_CREATE
    当前的PendingIntent不会主动创建,如果当前PendingIntent之前不存在(匹配的PendingIntent),那么获取PendingIntent失败。这个flag很少使用。
  • FLAG_CANCEL_CURRENT
    当前的PendingIntent如果存在(匹配的PendingIntent),那么它们都会被cancel,然后系统创建一个新的PendingIntent。对于通知栏来说,那些被cancel的消息单击后将无法打开。
  • FLAG_UPDATE_CURRENT
    当前PendingIntent如果已经存在(匹配的PendingIntent),那么它们都会被更新。即intent中的extras会被替换成最新的。

举例:
在manager.notify(id,notification)中,如果id是常量,那么多次调用notify只能弹出一个通知,后续的通知会把前面的通知完全替代。而如果每次id都不同,那么会弹出多个通知。
如果id每次都不同且PendingIntent不匹配,那么flags不会对通知之间造成干扰。
如果id不同且PendingIntent匹配:

  1. 如果采用了FLAG_ONE_SHOT标记位,那么后续通知中的PendingIntent会和第一条通知完全一致,包括extras,单击任何一条通知后,剩下的通知均无法再打开,当所有的通知被清除后,会再次重复这一过程。
  2. 如果采用FLAG_CANCEL_CURRENT,那么只有最新的通知可以打开。
  3. 如果采用FLAG_UPDATE_CURRENT,那么之前弹出的通知中的PendingIntent会被更新,与最新一条的通知完全一致,包括extras,并且这些通知都可以打开。

RemoteViews的内部机制

构造方法
public RemoteViews(String packageName, int layoutId)

第一个参数是当前应用的包名,第二个参数是待加载的布局文件。
RemoteViews并不支持所有的view类型,支持类型如下:

  • Layout:FrameLayout、LinearLayout、RelativeLayout、GridLayout。
  • View:AnalogClock、Button、Chronometer、ImageButton、ImageView、ProgressBar、TextView、ViewFlipper,ListView,GridView、StackView、AdapterViewFlipper、ViewStub。
  • RemoteViews不支持以上view的子类。

访问RemoteViews的view元素,必须通过一系列set方法完成:

方法名 作用
setTextViewText(int viewId,CharSequence text) 设置TextView的文本内容 第一个参数是TextView的id 第二个参数是设置的内容。
setTextViewTextSize(int viewId, int units, float size) 设置TextView的字体大小 第二个参数是字体的单位。
setTextColor(int viewId, int color) 设置TextView字体颜色。
setImageViewResource(int viewId, int srcId) 设置ImageView的图片
setInt(int viewId,String methodName, int value) 反射调用View对象的参数类型为Int的方法 比如上述的setImageViewResource的方法内部就是这个方法实现 因为srcId为int型参数。
setLong setBoolean 类似于setInt。
setOnClickPendingIntent(int viewId,PendingIntent pendingIntent) 添加点击事件的方法

大部分set方法是通过反射来完成的。

RemoteViews内部机制

通知栏和小组件分别由NotificationManager(NM)和AppWidgetManager(AWM)管理,而NM和AWM通过Binder分别和SystemService进程中的NotificationManagerService以及AppWidgetService中加载的,而它们运行在系统的SystemService中,这就和我们进程构成了跨进程通讯。

首先RemoteViews会通过Binder传递到SystemService进程,因为RemoteViews实现了Parcelable接口,因此它可以跨进程传输,系统会根据RemoteViews的包名等信息拿到该应用的资源;然后通过LayoutInflater去加载RemoteViews中的布局文件。接着系统会对View进行一系列界面更新任务,这些任务就是之前我们通过set来提交的。set方法对View的更新并不会立即执行,会记录下来,等到RemoteViews被加载以后才会执行。

为了提高效率,系统没有直接通过Binder去支持所有的View和View操作。而是提供一个Action概念,Action同样实现Parcelable接口。系统首先将View操作封装到Action对象并将这些对象跨进程传输到SystemService进程,接着SystemService进程执行Action对象的具体操作。远程进程通过RemoteViews的apply方法来进行View的更新操作,RemoteViews的apply方法会去遍历所有的Action对象并调用他们的apply方法。这样避免了定义大量的Binder接口,也避免了大量IPC操作。

apply和reApply的区别在于:apply会加载布局并更新界面,而reApply则只会更新界面。RemoteViews在初始化界面时会调用apply方法,后续更新界面调用reApply方法。

关于单击事件,RemoteViews中只支持发起PendingIntent,不支持onClickListener那种模式。setOnClickPendingIntent用于给普通的View设置单击事件,不能给集合(ListView/StackView)中的View设置单击事件(开销大,系统禁止了这种方式)。如果要给ListView/StackView中的item设置单击事件,必须将setPendingIntentTemplate和setOnClickFillInIntent组合使用才可以。

RemoteViews的意义

当一个应用需要更新另一个应用的某个界面,我们可以选择用AIDL来实现,但如果更新比较频繁,效率会有问题,同时AIDL接口就可能变得很复杂。如果采用RemoteViews就没有这个问题,但RemoteViews仅支持一些常用的View,如果界面的View都是RemoteViews所支持的,那么就可以考虑采用RemoteViews。

Demo AB

更多内容戳这里(整理好的各种文集)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,014评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,796评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,484评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,830评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,946评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,114评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,182评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,927评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,369评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,678评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,832评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,533评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,166评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,885评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,128评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,659评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,738评论 2 351

推荐阅读更多精彩内容