引言
我看到的分析RePlugin中BroadcastReceiver的文章中,基本都放在了对于插件中静态注册的BroadcastReceiver的处理上,却忽略了还有动态注册的BroadcastReceiver,而实际上这部分也很重要,而且replugin-plugin-gradle中有一项重要的内容就是将插件中LocalBroadcastManager的调用替换为PluginLocalBroadcastManager的调用。
1.插件中静态注册的BroadcastReceiver
在RePlugin解析之插件的安装与加载一文中有分析Loader中的loadDex()方法,在这个方法中有一个regReceivers()的调用,这个调用的目的就是动态注册插件中静态的BroadcastReceiver,这个方法如下:
|
|
注意mPluginHost只是IPluginHost的代理,通过它可以进行IPC调用到PmHostSvc中的regReceiver()方法,而这里显然就是将解析插件Manifest得到的BroadcastReceiver的IntentFilter信息传递过去, PmHostSvc.regReceiver()方法如下:
|
|
注意由于aidl中map不能带有类型,所以这里进行了强转。这个方法中有两个重点,第一个是saveAction(),即保存action-plugin-receiver的关系 ,它们的关系如下:
|
|
看完这个图之后就很好理解了。
regReceiver()中另外一个重点是BroadcastReceiver的注册,从这里可以看出,所有的广播都是注册在mReceiverProxy这个代理的广播接收器上的,它是PluginReceiverProxy对象,onReceive()方法如下:
|
|
这个方法很好理解,就是根据action获取map
|
|
可见这里只是起一个转接的作用,真正执行工作的是PluginReceiverHelper.onPluginReceiverReceived()方法:
|
|
这个方法很简单,就是通过插件的PluginDexClassLoader加载相应的BroadcastReceiver子类,然后创建对象,最后在UI线程中调用其onReceive()方法。
可见,只要RePlugin框架完成了初始化,那么插件中的静态广播和独立App的静态广播效果是一样的,即同样有拉起进程的作用。
2.插件中动态注册的BroadcastReceiver
2.1 更安全却不能IPC的LocalBroadcastManager
考虑到全局广播的安全隐患,Android系统之后推出了LocalBroadcastManager这个东东,它的接口也很简单,就是registerReceiver(BroadcastReceiver,IntentFilter), sendBroadcast(Intent), sendBroadcastSync(Intent), unregisterReceiver(BroadcastReceiver)方法。
下面我们就看看它的实现,首先是registerReceiver()方法:
|
|
可见,这里就是利用mReceivers保存receiver和它的IntentFilter的关系,同时利用mActions保存action和ReceiverRecord的关系,看一下mReceivers和mActions的定义会更清楚:
|
|
其中ReceiverRecord这个类的定义很简单:
|
|
非常简单的类,就是为了保存filter和receiver,不多说了。
而sendBroadcast()方法如下:
|
|
这个方法虽然有点长,实际上逻辑很简单,就是就是根据action找到所有的ReceiverRecord(在ReceiverRecord中含有BroadcastReceiver和IntentFilter信息),然后调用IntentFilter的match()方法进行包含action, type, scheme, data, categories在内的匹配,如果能够匹配上,就记录在receivers(List
|
|
而executePendingBroadcasts()方法如下:
|
|
这个方法很简单,就是遍历各个BroadcastReceiver然后逐个通知,可见LocalBroadcastReceiver确实更安全了,因为它只会在本进程中发送广播,但是使用其实也更受限制,即没有IPC能力。
还有一个unregisterReceiver()方法,太简单了就不分析了。
2.2 replugin-plugin-gradle中对于LocalBroadcastManager做的替换
对于插件中LocalBroadcastManager的替换是在replugin-plugin-gradle中的LocalBroadcastInjector中的,它的injectClass()方法如下:
|
|
if (filePath.contains('android/support/v4/content/LocalBroadcastManager'))
,保护性逻辑,避免替换掉v4包中的源码实现。pool.makeClass()
,创建当前类文件的CtClass实例。ctCls.defrost()
如果CtClass实例被冻结,则执行解冻操作。ctCls.getDeclaredMethods().each { }
和ctCls.getMethods().each { }
,遍历全部方法,并执行instrument
方法,逐个扫描每个方法体内每一行代码,并交由LocalBroadcastExprEditor
的edit()
处理对方法体代码的修改
而LocalBroadcastExprEditor如下:
|
|
就是在replaceStatement()中完成了对于LocalBroadcastManager中getInstance(), registerReceiver(), unregisterReceiver(), sendBroadcast()和sendBroadSync()等方法的调用替换为PluginLocalBroadcastManager的调用。
2.3 PluginLocalBroadcastManager分析
PluginLocalBroadcastManager这个方法其实很简单,它甚至直接copy了LocalBroadcastManager的逻辑,对外的接口也是那几个方法,只是在RePluginFramework完成初始化之前,调用它copy的这个逻辑,在完成初始化之后,则是通过反射调用LocalBroadcastManager,不过真的不太明白这样做的意义在哪,不能从一开始就调用LocalBroadcastManager吗,或者说干脆一直就用这个逻辑不行吗?