【学习】Android的RemoteViews

什么是RemoteViews

它表示的是一个View结构,可以在其他进程中显示,由于它在其他进程中显示,为了更新它的界面,它提供了一组基础的操作用于跨进程更新它的界面。

RemoteViews的应用

它在实际开发中主要用于通知栏和桌面小部件的开发过程中。通知栏主要是通过NotificationManager的notify方法来实现的,它除了默认效果外,还可以另外定义布局。桌面小部件的开发过程中通过AppWidgetProvider来实现,AppWidgetProvider本质上是一个广播。通知栏和桌面小部件的开发都会用到RemoteViews,它们在更新时无法像在Activity里直接去更新View,因为二者的界面都运行在其他进程中,确切来说是系统的SystemServer进程。RemoteViews提供了一系列set方法来跨进程更新界面,并且这些方法只是View全部方法的子集,另外它支持的View类型也是有限的。

1.RemoteViews在通知栏上的应用

通知渠道

在Android8.0中引入了通知渠道的概念,每一条通知都要属于一个对应的渠道。每个应用程序可以自由地创建当前应用拥有哪些通知渠道,但是这些渠道控制权掌握在用户受伤,用户可以自由选择这些通知的重要程度。

设置图标要用纯alpha层

使用系统默认的样式弹出一个通知(书比较老,有些方法已经废弃,下图使用如今新的方式构建通知)
在这里插入图片描述

显示效果
在这里插入图片描述

自定义通知

步骤
1.提供一个布局文件
2.通过RemoteViews来加载这个布局文件

在这里插入图片描述

在这里插入图片描述

显示效果

在这里插入图片描述

setAutoCancel方法用于控制通知点击后是否消失,还有setStyle,bigText等用法,更多API可以去查看官方文档。

如何更新RemoteViews

必须要通过RemoteViews所提供的一系列set方法来更新,想要为其添加单击事件,则要使用PendingIntent并通过setOnClickPendingIntent方法来实现。

2.RemoteViews在桌面小部件上的应用

AppWidgetProvider是Android中提供的用于实现桌面小部件的类,其本质是一个广播,即BroadcastReceiver。

步骤
1.定义小部件界面

在res/layout/下新建一个xml文件,内容自定义

2.定义小部件的配置信息

在res/xml下新建appwidget_provider_info.xml,名称随意选择,添加如下内容

在这里插入图片描述

initialLayout:小工具所使用的初始化布局
minHeight minWidth定义小工具的最小尺寸
updatePeriodMillis:定义小工具的自动更新周期,毫秒为单位

3.定义小部件的实现类

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

上面的代码实现了一个简单的桌面小部件,在小部件上面显示一张图片,单击它后这个图片就会旋转一周,当小部件被添加到桌面后,会通过RemoteViews加载布局文件,而当小部件被单及后的旋转效果则是通过不断地更新RemoteViews来实现的,由此可见桌面小部件不管是初始化还是后续的更新界面必须使用RemoteViews来实现

4.在AndroidManifest.xml声明小部件

因为它本质上是一个广播组件

在这里插入图片描述

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

除了onUpdate方法,还有onEnabled、onDisabled、onDeleted、onReceive。这些方法会自动地被onReceive方法在合适的时间调用。当广播到来的时候AppWidgetProvider会自动根据广播的Action通过onReceive方法来自动分发广播。

调用时机

onEnabled:当该窗口小部件第一次添加到桌面时调用该方法,可添加多次但只在第一次调用
onUpdate:小部件被添加时或者每次小部件更新时都会调用一次该方法,小部件的更新时机由opdatePeriodMills来指定,每个周期它都会自动更新一次
onDeleted:每删除一次桌面小部件就调用一次
onDisabled:当最后一个该类型的桌面小部件按被删除时调用该方法
onReceive:广播的内置方法,用于分发事件给其他方法,它会根据不同的Action来分别调用上方的方法

在这里插入图片描述

PendingIntent概述

pending表示的是一种待定、等待、即将发生的意思,PendingIntent是在将来某个不确定的时刻发生

三种待定意图

1.启动Activity
2.启动Service
3.发送广播

在这里插入图片描述

PendingIntent匹配规则

如果两个PendingIntent内部的Intent也相同并且requestCode也相同,那么这两个PendingIntent就是相同的。

Intent匹配规则

两个Intent的CompnentName和intent-filter都相同,那么这两个Intent就是相同的。Extras不参与Intent的匹配过程,只要Intent之间的CompnentName和intetn-filter相同,即使他们的extras不同,那么这两个Intent也是相同的。

FLAGS参数

FLAG_ONE_SHOT

当前描述的PendingIntent只能被使用一次,然后它就会被自动cancel,如果后续还有相同的PendingIntent,那么它们的send方法就会调用失败。对于通知栏来说,同类的通知只能使用一次,后续通知点击无法打开

FLAG_NO_CREATE

当前描述的PendingIntent不会主动创建,如果当前PengdingIntent之前不存在,那么getActivity、getService、getBroadcast方法会直接返回null,即获取PendingIntent失败。这个标记很少见,无法单独使用

FLAG_CANCEL_CURRENT

当前描述的PendingIntent如果已经存在,那么它们都会被cancel,然后系统会创建一个新的PendingIntent。对于通知栏消息来说,那些被cancel的消息单击后将无法打开

FLAG_UPDATE_CURRENT

当前描述的PendingIntent如果已经存在,那么他们都会被更新,即它们Intent中的Extras都会被替换成新的

manager.notify(1,notification)中,如果第一个参数id是常量,那么多次调用notify都只能弹出一个通知,后续的通知会把前面的通知完全替代掉,而如果每次id都不同,那么多次调用notify会弹出多个通知。
在这里插入图片描述

RemoteViews的内部机制

RemoteViews支持的View类型
Layout
FrameLayout LinearLayout RelativeLayout GridLayout

View
AnalogClock Botton Chronometer ImageButton ImageView ProgressBar TextView ViewFlipper ListView GridView StackView AdapterViewFlipper ViewStub

RemoteViews不会支持它们的子类以及其它View类型

RemoteView的部分set方法

在这里插入图片描述

RemoteViews的工作过程

通知栏和桌面小部件分别由NotificationManager和AppWidgetProvider管理,而它们分别通过Binder和SystemServer进程中的NotficationManagerService以及AppWidgetService进行通信。由此可见,通知栏和桌面小部件中的布局文件实际上是在NotificationManagerService和AppWidgetService中被加载的,而它们运行在系统的SystemServer中,这就和我们的进程构成了跨进程通信的场景。

首先RemoteViews会通过Binder传递到SystemServer进程,这是因为RemoteViews实现了Parcelable接口,因此它可以跨进程传输,系统会根据RemoteViews中的包名等信息会得到该应用的资源。然后会通过LayoutInflater去加载RemoteViews中的布局文件。在SystemServer进程中加载后的布局是一个普通的View,只不过相对于我们的进程它是一个RemoteViews而已。接着系统会对View执行一系列界面更新任务,也就是我们之前通过set方法提交的更新操作,更新不是立刻执行的,在RemoteViews内部会记录所有的更新操作,具体的执行时机要等到RemoteViews被加载以后才能执行,这样RemoteViews就可以在SystemServer进程中显示了。当需要更新RemoteViews时,我们需要调用一系列set方法并通过NotificationManager和AppWidgetManager来提交更新任务,具体更新操作也是在SystemServer完成的

从理论上来说,系统完全可以通过Binder去支持所有的View和View操作,但这样代价太大,因为View方法太多,而且大量IPC操作会影响操作效率。但是系统提供了一个Action的概念。Action代表一个View操作,Action同样实现了Parcelable接口。系统首先将View操作封装到Action对象,当我们通过NotificationManager和AppWidgetManager来提交我们的更新时,这些Action对象就会传输到远程进程并在远程进程中依次执行。远程进程通过RemoteViews的apply方法,具体的View更新操作是由Action对象的apply方法来完成的。上述做法的好处是显而易见的,首先不需要定义大量的Binder接口,其次通过在远程进程中批量执行RemoteViews的修改操作从而避免了大量的IPC操作,提高了程序的性能。

在这里插入图片描述

查看set方法源码(取其一)

在这里插入图片描述

在这里插入图片描述

viewId是被操作的View的id,"setText"是方法名,text是要给textview设置的文本,从setCharSequence的实现可以发现它内部并没有对View进程进行直接操作,而是添加了一个ReflectionAction对象,这应该是一个反射类型的动作。

在这里插入图片描述

从上图代码我们可以看到RemoteViews内部有一个mActions成员,它是一个ArrayList,外界每调用一次set方法,RemoteViews就会为其创建一个Acition对象并加入到ArrayList中。这里仅仅是将Action对象保存起来,并未对View进行实际的操作。

RemoteViews的apply方法

在这里插入图片描述

从上图可以看出它会调用inflateView方法去加载布局文件,然后通过performApply执行更新操作

在这里插入图片描述

performApply方法中它遍历mAction这个列表并执行每个Action对象的apply方法,Action对象的apply方法就是真正操作View的地方。

RemoteViews在通知栏和桌面小部件的工作过程和上面描述一致,当我们调用RemoteViews的set方法时,并不会立刻更新它们的界面,必须要通过NotificationManager的notify方法才能更新它们的界面。实际上在AppWidgetManager的updateAppWidget内部实现中,它们的确是通过RemoteViews的apply以及reapply方法来加载或者更新界面,apply和reapply的区别是apply会加载布局并更新界面,而reapply则只会更新界面。通知栏和小插件在初始化界面时会调用apply发明方法,而在后续的更新界面时则会调用reapply方法。

ReflectionAction中apply源码
在这里插入图片描述

ReflectionAction表示一个反射动作,通过他对View的操作会以反射的方式来调用,其中getMethod就是根据方法名来得到反射所需的Method对象。使用ReflctionAction的set方法有setTextViewTect setBoolean setLong setDouble等。除了ReflectionAction还有T而TextViewSizeAction ViewPaddingAction SetOnClickPendingIntent等。

TextViewSizeAction部分实现
在这里插入图片描述

之所以不用反射模式因为setTextSize有两个参数,无法复用ReflectionAction,因为ReflectionAction的反射调用只有一个参数。

setOnClickPendingIntent
用于给普通View设置单击事件,但不能给集合中的View(ListView和StackView)设置单击事件,因为开销大,若想为它们提供单击事件,则需要和setPendingIntentTemplate和setOnClickFillInIntent组合使用才可以

RemoteViews用法核心代码

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

当应用界面中都是一些简单的View,两个应用之间使用RemoteViews会比AIDL效率高。
apply方法若使用资源id去加载布局,在两个应用之间有可能id不一样,但我们可以通过资源名称来加载布局文件,两个应用要约定好RemoteViews中的布局文件的资源名称如"layout_main",然后在根据名称查找到对应的布局文件并加载,然后调用reapply方法把一个应用中的View所做的一系列更新操作全部作用到另一个应用加载的View上面。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值