RemoteViews支持在其他进程显示和更新View,由于RemoteViews没有findViewById的方法,因为它是远程的View,即使有findViewById我们也不知道远程app的资源文件id 所以如果想要更新View的内容 就要使用RemoteViews提供的一系列set方法:
![img_e9f0ebed2b98b2dd8385f6e0e040f4e7.png](https://i-blog.csdnimg.cn/blog_migrate/25e1a68ec227031c68e82738d2f48ebd.png)
RemoteViews每调用用一个set方法都会添加一个Action到Action列表中,Action也是序列化的,也可以通过Binder传到远程。当执行Notification的notify方法后,就会调用RemoteView的apply方法,该方法会遍历每个Action,执行Action的 apply函数,apply函数会通过反射的方式调用RemoteViews的子子view的method,并进行设置,从而完成布局更新。
进程的通信一般基于Binder机制,在使用RemoteViews的时候将RemoteViews传给远程进程,所以RemoteViews是可以序列化的,系统会根据RemoteViews的包名,去创建Inflater类,并根据layoutId创建出view,然后应用apply方法来更新布局的内容。理论上Binder可以支持View的所有操作,但是太麻烦,需要提供更多的IPC操作,降低IPC的效率。所以Android系统在实现界面更新的时候没有每调用一次set操作就进行一次IPC操作,而是在调用诸如NotificationManager的notify的时候,进行批量更新。例如,如下代码创建一个RemoteViews并设置TextView。
![img_d875c1a3da29178f49620dad15b24f27.png](https://i-blog.csdnimg.cn/blog_migrate/1b964c35c4dfbce321e1a4fbe77eff87.png)
在调用setTextViewText的时候,RemoteViews的方法如下:
![img_2747a6bcd5abf566bae7da2a6015f129.png](https://i-blog.csdnimg.cn/blog_migrate/568c0c400a51c3704cda392427dfc0dd.png)
上面代码将setTextViewText转换成一个ReflectionAction,包含了textview_id、setText方法、value值。当调用appWidgetManager.updateAppWidget的时候就将remoteViews提交给远程进程,之后远程进程会执行RemoteViews的apply方法,如下所示:
![img_21e3432f7cdb52cf5591c7975433254a.png](https://i-blog.csdnimg.cn/blog_migrate/405449648bcfdc40d71c76f4ef5aca46.png)
RemoteViews的apply方法分为以下几步:
1、获取RemoteViews对象
2、获取Inflater布局加载服务
3、根据RemoteViews中的layoutId,加载布局
执行RemoteViews的performApply,执行Action列表的所有apply操作,进行View更新
![img_b7f2065cd634fe14b870a198a0767168.png](https://i-blog.csdnimg.cn/blog_migrate/14c512c3706305e02d72867a8d406255.png)
参数root也就是RemoteViews的layoutId对应的父view,viewId也就是要查找的子view,这里就是id为text_1的TextView,Action的apply方法首先find出TextView,然后反射调用他的setText方法,进行value设置。
RemoteViews的apply和reapply的区别,apply多了一个创建布局的步骤,即创建Inflater后再inflate布局,之后再加载内容;而replay只会更新布局,不会创建新的布局。