BroadcastReceiver插件化

本文首先介绍了BroadcastReceiver的基础知识,包括静态和动态注册、显式与隐式广播、有序与无序广播的区分。然后重点讨论了BroadcastReceiver的插件化原理,即如何将静态注册转换为动态注册,通过分析VirtualApk库中的实现方式,揭示了将AndroidManifest.xml中的BroadcastReceiver动态注册的过程。

BroadcastReceiver基础知识

同样地,在讲解BroadcastReceiver插件化之前,我们先来回顾一下BroadcastReceiver的基础知识。

发送一个广播比较简单。

val intent=Intent("com.example.example")//实际使用过程中将action名传入Intent构造方法
sendBroadcast(intent)

广播按照不同标准可以分为几类。

  • 根据注册方式进行划分

    • 静态注册。

      该方法是指在AndroidManifest文件进行注册。

      <receiver
                  android:name=".ExampleBroadCastReceiver"
                  android:enabled="true"
                  android:exported="true">
                  <intent-filter>
                      <action android:name="android.intent.action.BATTERY_LOW">
                      </action>
                  </intent-filter>
      </receiver>
      
      • name属性。对应该BroadCastReceiver的名字。
      • enabled属性。代表该BroadCastReceiver是否启动。
      • exported属性。代表该BroadCastReceiver是否接收其它应用的广播。
      • intent-filter属性。代表该BroadCastReceiver想要接受的广播。
    • 动态注册。

      动态注册的步骤有两步。

      • 新建类继承于BroadCastReceiver并复写onReceive()方法。

      • 实例化BroadCastReceiver并进行注册。

        val exampleBroadCastReceiver=ExampleBroadCastReceiver()
        val intentFilter=IntentFilter()
        intentFilter.addAction("android.intent.action.BATTERY_LOW")
        registerReceiver(exampleBroadCastReceiver,intentFilter)
        

    二者使用方式看起来其实区别不大,那么在实际开发过程中我们应该如何选择呢?

    静态注册更常见的应用场景是应用未启动时接收系统广播,比如说手机开机完成的系统广播,手机刚开完机时,应用肯定还没有启动,这时候应用就可以通过静态广播来接收手机已经开机的系统广播,并执行相应的操作。

    动态注册相比于静态注册的优势就在于一个动字,它可以动态改变它感兴趣,也就是它响接收的广播。

  • 根据指不指定接收对象来进行划分。

    • 显式广播。该类广播需要指定接收对象,发送显式广播比较简单,只需要在发送广播时调用IntentFilter::setPackageName()方法,然后传入接收应用的包名即可。
    • 隐式广播。该类广播不需要指定接收对象,属于是广播界的老海王了,发给所有应用,需要注意的是,自安卓8.0以来,为了防止极个别恶意软件自启动,除了极少数个别系统广播之外,系统并不允许静态BroadcastReceiver接收隐式广播,
  • 根据是否有序来进行划分

    • 标准广播。这类广播又称为无序广播,标准广播发出后,所有感兴趣的BroadcastReceiver几乎会在同一时间收到该广播。

    • 有序广播。这类广播发出后,所有感兴趣BroadcastReceiver会按照优先级先后收到该广播,需要注意的是,收到有序广播的BroadcastReceiver可以选择继续往下传,也可以选择中断该广播,也就是说优先级比它低的BroadcastReceiver都不会收到广播,有序广播发送比较简单。

      sendBroadcast(intent)//标准广播
      sendOrderedBroadcast(intent)//有序广播
      

      同样地,优先级可以静态设置,也可以动态设置。

      /*静态设置*/
      <receiver
        android:name=".ExampleBroadCastReceiver"
        android:enabled="true"
        android:exported="true">
        <intent-filter android:priority="100">
          <action android:name="android.intent.action.BATTERY_LOW">
          </action>
        </intent-filter>
      </receiver>
      
      //动态设置
      val intentFilter=IntentFilter()
      intentFilter.addAction("android.intent.action.BATTERY_LOW")
      intentFilter.priority=100
      

      中断有序广播只需要在onReceive()方法中添加一行代码。

      abortBroadcast();
      

BroadcastReceiver插件化原理

BroadcastReceiver插件化原理比较简单,一言以蔽之,就是将静态注册的BroadcastReceiver转化为动态注册。

同样地,我们会分析VirtualApk第三方库中的BroadcastReceiver插件化第三方实现方案,VirtualApk的初始化和常用类详情可见我上一篇文章ContentProvider插件化

在该库中有一个类LoadedPlugin,其构造方法就会将静态注册的BroadcastReceiver转化为动态注册。

    public LoadedPlugin(PluginManager pluginManager, Context context, File apk) throws Exception {
        this.mPackage = PackageParserCompat.parsePackage(context, apk, PackageParser.PARSE_MUST_BE_APK);
        ...
        // Register broadcast receivers dynamically
        //1  
        Map<ComponentName, ActivityInfo> receivers = new HashMap<ComponentName, ActivityInfo>();
        for (PackageParser.Activity receiver : this.mPackage.receivers) {
            receivers.put(receiver.getComponentName(), receiver.info);
    
            BroadcastReceiver br = BroadcastReceiver.class.cast(getClassLoader().loadClass(receiver.getComponentName().getClassName()).newInstance());
            for (PackageParser.ActivityIntentInfo aii : receiver.intents) {
                this.mHostContext.registerReceiver(br, aii);
            }
        }
        this.mReceiverInfos = Collections.unmodifiableMap(receivers);
        this.mPackageInfo.receivers = receivers.values().toArray(new ActivityInfo[receivers.size()]);
        ...
    }

代码核心思想是利用PackageParse::parsePackage()解析Apk中的AndroidManifest.xml文件,获取各个BroadcastReceiver的信息,并进行动态注册。

需要注意的是,1处代码之所以键值对的value为ActivityInfo,是因为PackageParse在解析AndroidManifest.xml文件时会把标签当作标签进行解析。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值