美文网首页
RemoteView

RemoteView

作者: 卡路fly | 来源:发表于2020-05-28 15:43 被阅读0次

    来源:https://www.jianshu.com/p/fc237e90cd5f(吹爆)

    介绍

    远程View,它表示的是一个View结构,它可以在其他进程中显示,为了跨进程更新它的界面,RemoteViews提供了一组基础的操作来实现这个效果。

    RemoteViews在Android中的使用场景有两种:

    • 通知栏
    • 桌面小部件

    桌面小部件

    1. 定义小部件布局
    2. 定义小部件配置信息< appwidget-provider
    属性 含义
    android:initialLayout 指定小部件的初始化布局
    android:minHeight 小部件最小高度
    android:minWidth 小部件最小宽度
    android:previewImage 小部件列表显示的图标
    android:updatePeriodMillis 小部件自动更新的周期
    android:widgetCategory 小部件显示的位置,home_screen表示只在桌面上显示
    1. 定义小部件的实现类,extends AppWidgetProvider
    2. 清单文件声明
    <receiver android:name=".CustomAppWidgetProvider">
        <meta-data
            android:name="android.appwidget.provider" // name固定
            android:resource="@xml/app_widget_provider_info" /> // xml
    
        <intent-filter>
            <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> // 必须加的,它作为小部件的标识存在
            <action android:name="com.shenhuniurou.appwidgetprovider.click.one" /> // 其他action对应点击动作
            <action android:name="com.shenhuniurou.appwidgetprovider.click.two" />
            <action android:name="com.shenhuniurou.appwidgetprovider.click.three" />
            <action android:name="com.shenhuniurou.appwidgetprovider.click.four" />
        </intent-filter>
    </receiver>
    

    总结:

    1. 当小部件一被添加到桌面时会调用Provider中的onUpdate方法,在这个方法中我们会通过AppWidgetManager去更新小部件的界面
    2. 通过RemoteViews来操作,setOnClickPendingIntent给每个按钮设置了点击时会发送的广播动作,而在清单文件中我们声明小部件时已经将这些广播动作都加到intent-filter,所以当我们点击桌面上该小部件中的某个按钮时,就会发送对应的广播,而小部件监听了这个广播,接收到广播后再onReceive方法中根据动作来分别处理点击事件。
    3. 对小部件的一些其他操作方法(比如onEnabled、onDisabled、onDeleted)的广播也会在onReceive中接收到,然后分发给不同的方法。

    PendingIntent

    将要发生的意图,和Intent的区别就在于一个是立即执行的一个是在未来某个时候执行。(通知中点击通知时跳转页面)

    给RemoteViews设置点击事件,就必须使用PendingIntent,通过setOnClickPendingIntent方法来设置。
    PendingIntent是通过send和cancel方法来发送和取消待执行的Intent。

    PendingIntent支持三种待定意图:

    1. 启动activity(常见的通知)
      启动Activity它有两种,启动单个和启动多个,当使用getActivities时,实际上启动的是Intent数组中最后一个activity,如果要让最后一个activity返回时不退出app而是退回到上一个activity,实现方式可参照我上面第一个按钮的点击处理。

    2. 启动Service

    3. 发送广播

    PendingIntent 方法参数

    getActivity、getService、getBroadcast这三个方法的参数意义都是相同的,第一个上下文,第三个待定的意图,第二个requestCode表示PendingIntent发送方的请求码,多数情况下设置为0即可,另外requestCode会影响到第四个参数flags的效果。flags这个标志位表示执行效果。

    PendingIntent的匹配规则
    如果两个PendingIntent它们内部的Intent相同,且requestCode也相同,那么这两个PendingIntent就是相同的;
    Intent相同的情况
    如果两个Intent的ComponentName和intent-filter都相同,那么这两个Intent就是相同的
    (Extras不参与Intent的匹配过程,就是它不同,只要ComponentName和intent-filter相同,Intent都算相同的。)

    flags 执行效果

    FLAG_ONE_SHOT:表示当前描述的PendingIntent只能被使用一次,然后它就会自动cancel,如果后续还有相同的PendingIntent,那么它们的send方法就会调用失败。如果通知栏消息使用这种标记位,同类型的通知就只会被打开一次,后续的通知将无法点开。

    FLAG_NO_CREATE:表示当前描述的PendingIntent不会主动创建,如果当前PendingIntent之前不存在,那么getActivities等这些方法会直接返回null,获取PendingIntent失败。它无法单独使用。

    FLAG_CANCEL_CURRENT:表示当前描述的PendingIntent如果已经存在,就cancel它,然后系统会创建一个新的。

    FLAG_UPDATE_CURRENT:表示当前描述的PendingIntent如果已经存在,那么它会被更新,内部的Intent中的Extras也会被更新。

    RemoteViews的内部机制

    通知栏和桌面小部件分别由NotificationManager和AppWidgetManager来管理的,而NotificationManager和AppWidgetManager是通过Binder分别和SystemServer进程中的NotificationManagerService以及AppWidgetService进行通信。
    因此,通知栏和桌面小部件中的布局文件实际上是在NotificationManagerService和AppWidgetService中被加载的,而他们运行在SystemServer中,这其实已经和我们自己的app进程构成了跨进程通信。

    理论分析

    1. 首先RemoteViews会通过Binder传递到SystemServer进程
      (因为RemoteViews实现了Parcelable接口,可以跨进程传输)
    2. 系统会根据RemoteViews中的包名等信息去获取到该app的资源
    3. 然后通过LayoutInflater去加载RemoteViews中的布局文件。
      在SystemServer进程中加载后的布局文件是一个普通的View,只不过对于我们的app进程来说,它是一个远程View也就是RemoteViews。
    4. 接着系统会对View执行一系列界面更新任务
      这些任务就是之前我们通过set方法提交的,set方法对View的更新操作并不是立刻执行的
      在RemoteViews内部会记录所有的更新操作,具体的执行要等到RemoteViews被完全加载以后,这样RemoteViews就可以在SystemServer中进程中显示了,这就是我们所看到的通知栏消息和桌面小部件。
      当需要更新RemoteViews时,我们又需要调用一系列set方法通过NotificationManager和AppWidgetManager来提交更新任务,具体更新操作也是在SystemServer进程中完成的。

    理论上讲系统完全可以通过Binder去支持所有的View和View操作,但是这样做代价太大,View的方法太多了,另外大量的IPC操作会影响效率。

    为了解决这个问题,系统并没有通过Binder去直接支持View的跨进程访问,而是提供了一个Action的概念。

    Action代表一个View操作,Action同样实现了Parcelable接口。系统首先将View操作封装到Action对象并将这些对象跨进程传输到远程进程,接着在远程进程中执行Action对象中的具体操作。

    在我们的app中每调用一次set方法,RemoteViews中就会添加一个对应的Action对象,当我们通过NotificationManager和AppWidgetManager来提交我们的更新时,这些Action对象就会传输到远程进程并在远程进程中依次执行。

    远程进程通过RemoteViews的apply方法来进行View的更新操作,apply方法内部是去遍历所有的Action对象并调用它们的apply方法,具体的View更新操作是由Action对象的apply方法来完成。

    好处

    1. 不需要定义大量的Binder接口
    2. 通过在远程进程中批量执行RemoteViews的更新操作从而避免了大量的IPC操作,这就提高了程序的性能。

    RemoteViews的优缺点

    优点:实际开发中,跨进程通信我们可以选择AIDL去实现,但是如果对界面的更新比较频繁,这时会有效率问题,而且AIDL接口可能会变得很复杂,但如果采用RemoteViews来实现就没有这个问题了

    缺点:仅支持一些常见的View,而对于自定义View是不支持的。

    相关文章

      网友评论

          本文标题:RemoteView

          本文链接:https://www.haomeiwen.com/subject/ygkaahtx.html