0%

第11章:BroadcastReceiver的插件化解决方案

Receiver概述

Receiver分为动态和静态两种,简单讨论下区别:

  • 静态广播在 AndroidManifest 中注册,因为Android系统重启时,PMS都会解析App中的AndroidManifest,所以静态广播都存在于 PMS 中
  • 动态广播通过 Context 的registerReceiver 方法最终调用 AMN.getDefault().registerReceiver 方法,所以,动态广播的注册信息存在于 AMS 中

除了注册方式不一样,后续发送和接收的过程就一样了。整个过程简单如下:1、Context 发送广播,最终通过 AMN.getDefault().broadcastIntent,把要发送的广播告诉AMS。2、AMS收到消息后,根据intent-filter 筛选 PMS 和 AMS (即静态广播和动态广播)中符合条件的接收器,通知App进程启动这些广播(调用这些广播的 onReceive)。

动态广播的插件化解决方案

对于动态广播,我们只需要确保宿主App能加载插件中的这个动态广播类就行(因为这里并不需要直接与AMS打交道,只是个类而已)。通过9.3节的dex合并技术,就能做到了。

静态广播解决方案

静态广播无法像Activity那样,即使没在AndroidManifest 中注册也能生效,因为无论是注册还是发送广播,都必须有 IntentFilter,其中的action是可以随意设置的,所以我们对于 Receiver 压根就不能通过类似 Activity 的插桩方式。只有另辟蹊径。

静态广播当做动态广播处理

具体分为两步:

  1. PMS 只能读取宿主 App 的AndroidManifest 文件,读取其中的静态广播并注册。我们可以反射,手动控制PMS读取插件AndroidManifest 中声明的静态广播列表。
  2. 遍历这个静态广播列表,使用 classLoader 加载列表中每个广播类,实例化成一个对象,然后作为动态广播注册到AMS中

静态广播的插件化终极解决方案

上述静态广播当做动态广播的方案,这丧失了静态广播的特性——不需要启动App就可以启动一个静态广播。所以我们仍要探寻如何不启动App也能和插件中的静态广播通信。

回忆一下前面介绍的Activity 和 Service 插件化的占位思想:

  • Activity 只需要一个占位 StubActivity 就能面对大部分插件Activity了,对于LaunchMode 的其他三种形式,则需要更多的占位 StubActivity 应对
  • Service 也需要占位 StubService ,但是一个 StubService 只能对应一个插件中的Service,所以我们需要在宿主App中占位多个 StubService,通过json来配置映射关系。

如果也用占位的思想,每个静态广播需要携带一个或者多个Action,StubReceiver 也不例外,如果 StubReceiver 和插件中的静态广播是一对多的关系,那么从外界发送一个广播到App,就会触发插件中的所有静态广播。由此得出:StubReceiver 和插件中的广播只能是一对一的关系

不过,我们可以为一个广播设置多个Action,这样我们就不需要预先创建很多个StubReceiver用来面对插件中的静态广播了,只需要一个 StubReceiver ,为它配置很多个 action 即可。这样,插件中的静态广播就要和这些 action 建立一对一的关系,还是以前的思路,使用Json配置映射关系就行

还有,AndroidManifest 中支持为每个组件配置 metadata,利用这个特性,为插件中每个静态广播配置对应的 StubReceiver 中的 action ,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<receiver 
android:name=".MyReceiver"
android:enabled="true"
android:exported="true" >
<intent-filter>
<action android:name=baobao/>
</intent-filter>
<meta-data android:name="oldAction" android:value="jianqiang1"></meta-data>
</receiver>

<receiver
android:name=".MyReceiver2"
android:enabled="true"
android:exported="true" >
<intent-filter>
<action android:name="baobao2" />
</intent-filter>
<meta-data android:name="oldAction" android:value="jianqiang2"></meta-data>
</receiver>

解析插件中的 AndroidManifest 文件,可以借助11.3章节介绍的ReceiverHelper 类的 preLoadReceiver 方法,在遍历插件中的每个静态的 Receiver 时,去除Receiver 的metadata 数据,根据oldAction值,对应到Receiver,比如 MyReceiver 对应 jianqiang1,MyReceiver2 对应 jianqiang2 。

之后,把插件中的Receiver 手动注册为动态广播。宿主中定义的StubReceiver占位广播的作用是分发

这就解决了静态广播的问题,我们可以在App没启动的时候,就启动插件中的静态广播(我其实还是没看懂怎么在没有启动App的情况,就能启动这个静态广播,后续长丝下)。美中不足的是,这个StubReceiver 需要配置很多个 Action 。

谢谢你的鼓励