1. RemoteViews 所支持的 View 的类型
RemoteViews 目前并不能支持所有的 View 类型,它所支持的所有的类型如下:
Layout
View
FrameLayout
、LinearLayout
、RelativeLayout
、GridLayout
AnalogClock
、Button
、Chronometer
、ImageButton
、ImageView
、ProgressBar
、TextView
、ViewFlipper
、ListView
、GridView
、StackView
、AdapterViewFlipper
、ViewStub
RemoteViews 不支持上面所列 View 的子类及其他 View 类型,包括自定义类型 View 也不支持。比如如果在通知栏的 RemoteViews 中使用系统的 EditText,那么通知栏消息将无法弹出且会抛出异常
2. RemoteViews 常用的 set 方法
方法名 | 作用 |
---|---|
setTextViewText(int viewId, CharSequence text) |
设置 TextView 的文本 |
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 的方法 |
setLong(int viewId, String methodName, long value) |
反射调用 View 对象的参数类型为 long 的方法 |
setBoolean(int viewId, String methodName, boolean value) |
反射调用 View 对象的参数类型为 boolean 的方法 |
setOnClickPendingIntent(int viewId, PendingIntent pendingIntent) |
为 View 添加单击事件,事件类型只能为 PendingIntent |
3. RemoteViews 的内部机制
RemoteViews 主要作用于通知栏和桌面小部件,以这两个为例
通知栏和桌面小部件分别由
NotificationManager
和AppWidgetManager
管理,而NotificationManager
和AppWidgetManager
通过Binder
分别和SystemServer
进程中的NotificationManagerService
以及AppWidgetService
通信由此,通知栏和桌面小部件中的布局文件实际上是在
NotificationManagerService
和AppWidgetService
中被加载的,而它们运行在系统的SystemServer
中,这就和我们的进程构成了跨进程通信的场景- 首先 RemoteViews 会通过 Binder 传递到 SystemServer 进程,这是因为 RemoteViews 实现了 Parcelable 接口,因此它可以跨进程传输,系统会根据 RemoteViews 中的包名等信息去得到该应用的资源
- 然后会通过 LayoutInflater 去加载 RemoteViews 中的布局文件,在 SystemServer 进程中加载后的布局文件是一个普通的 View,只不过相对于我们的进程它是一个 RemoteViews 而已
- 接着系统会对 View 执行一系列的界面更新任务,这些任务就是之前我们通过 set 方法来提交的。set 方法对 View 所做的更新不是立刻执行的,在 RemoteViews 内部会记录所有的更新操作,具体的执行要等到 RemoteViews 被加载以后才能执行,这样 RemoteViews 就可以在 SystemServer 进程中显示了,这就是我们所看到的通知栏消息或者桌面小部件
- 当需要更新 RemoteViews 时,我们需要调用一系列的 set 方法并通过 NotificationManager 和 AppWidgetManager 来提交更新任务,具体的更新操作也是在 SystemServer 进程中完成的
RemoteViews 内部机制示意图
关于单击事件,RemoteViews 只支持发起
PendingIntent
,不支持onClickListener
那种模式setOnClickPendingIntent
、setPendingIntentTemplate
以及setOnClickFillInIntent
之间的区别和联系setOnClickPendingIntent
用于给普通 View 设置单击事件,但是不能给集合(ListView 和 StackView)中的 View 设置单击事件,因为开销比较大,所以系统禁止了这种方式- 如果要给 ListView 和 StackView 中的 item 添加单击事件,必须将
setPendingIntentTemplate
和setOnClickFillInIntent
组合使用才可以