BroadcastReceiver插件化解决方案

  • 2019 年 10 月 7 日
  • 筆記

1.静态广播和动态广播仅区别于注册方式的不同。静态广播的注册信息保存在PMS中,动态广播的注册信息保存在AMS中

2.发送广播,也就是Context的sendBroadcast方法,最终会调用AMN.getDefault().broadcastIntent,把要发送的广播告诉AMS;

  AMS在收到上述信息后,搜索AMS和PMS中保存的广播,看哪些广播符合条件,然后通知App进程启动这些广播,也就是调用这些广播的onReceive方法

3.无论发送广播还是接受广播,都携带一个筛选条件:intent-filter。

<receiver      android:name=".MyReceiver"      android:enabled="true"      android:exported="true">      <intent-filter>          <action android:name="baobao2"/>  </receiver>
MyReceiver myReceiver = new MyReceiver();  IntentFilter intentFilter = new IntentFilter();  intentFilter.addAction("baobao2");  registerReceiver(myReceiver,intentFilter);

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

使用前面介绍的dex合并技术,插件中的动态广播就可以被宿主App正常调用了

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

1)PMS只能读取宿主App的AndroidManifest文件,读取其中的静态广播并注册。我们可以通过反射,手动控制PMS读取插件的AndroidManifest中声明的静态广播列表

2)遍历这个静态广播列表。使用插件的classLoader加载列表中的每个广播类,实例化成一个对象,然后作为动态广播注册到AMS中

public final class ReceiverHelper {        private static final String TAG = "ReceiverHelper";        /**       * 解析插件Apk文件中的 <receiver>, 并存储起来       *       * @param apkFile       * @throws Exception       */      public static void preLoadReceiver(Context context, File apkFile) {          // 首先调用parsePackage获取到apk对象对应的Package对象          Object packageParser = RefInvoke.createObject("android.content.pm.PackageParser");          Class[] p1 = {File.class, int.class};          Object[] v1 = {apkFile, PackageManager.GET_RECEIVERS};          Object packageObj = RefInvoke.invokeInstanceMethod(packageParser, "parsePackage", p1, v1);            // 读取Package对象里面的receivers字段,注意这是一个 List<Activity> (没错,底层把<receiver>当作<activity>处理)          // 接下来要做的就是根据这个List<Activity> 获取到Receiver对应的 ActivityInfo (依然是把receiver信息用activity处理了)          List receivers = (List) RefInvoke.getFieldObject(packageObj, "receivers");            for (Object receiver : receivers) {              registerDynamicReceiver(context, receiver);          }      }        // 解析出 receiver以及对应的 intentFilter      // 手动注册Receiver      public static void registerDynamicReceiver(Context context, Object receiver) {          //取出receiver的intents字段          List<? extends IntentFilter> filters = (List<? extends IntentFilter>) RefInvoke.getFieldObject(                  "android.content.pm.PackageParser$Component", receiver, "intents");            try {              // 把解析出来的每一个静态Receiver都注册为动态的              for (IntentFilter intentFilter : filters) {                  ActivityInfo receiverInfo = (ActivityInfo) RefInvoke.getFieldObject(receiver, "info");                    BroadcastReceiver broadcastReceiver = (BroadcastReceiver) RefInvoke.createObject(receiverInfo.name);                  context.registerReceiver(broadcastReceiver, intentFilter);              }          } catch (Exception e) {              e.printStackTrace();          }      }  }

***不启动App和插件中的静态广播通信***

在宿主的androidmanifest中注册占位StubReceiver

<application      android:name="jianqiang.com.receiverhook.UPFApplication"      android:allowBackup="true"      android:icon="@mipmap/ic_launcher"      android:label="@string/app_name"      android:supportsRtl="true"      android:theme="@style/AppTheme">      <activity android:name=".MainActivity">          <intent-filter>              <action android:name="android.intent.action.MAIN" />                <category android:name="android.intent.category.LAUNCHER" />          </intent-filter>      </activity>        <receiver          android:name=".StubReceiver"          android:enabled="true"          android:exported="true">          <intent-filter>              <action android:name="jianqiang1" />          </intent-filter>          <intent-filter>              <action android:name="jianqiang2" />          </intent-filter>          <intent-filter>              <action android:name="jianqiang3" />          </intent-filter>          <intent-filter>              <action android:name="jianqiang4" />          </intent-filter>          <intent-filter>              <action android:name="jianqiang5" />          </intent-filter>          <intent-filter>              <action android:name="jianqiang6" />          </intent-filter>          <intent-filter>              <action android:name="jianqiang7" />          </intent-filter>          <intent-filter>              <action android:name="jianqiang8" />          </intent-filter>          <intent-filter>              <action android:name="jianqiang9" />          </intent-filter>          <intent-filter>              <action android:name="jianqiang10" />          </intent-filter>      </receiver>  </application>

插件的androidmanifest中注册

<application      android:allowBackup="true"      android:icon="@mipmap/ic_launcher"      android:label="@string/app_name"      android:supportsRtl="true"      android:theme="@style/AppTheme">        <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>  </application>

具体流程是宿主清单文件中查找jianqiang1,然后到插件的清单文件中查找jianqiang1,最后找到jianqiang1对应的baobao,这才是真正要注册的广播

实现逻辑如下

public final class ReceiverHelper {        private static final String TAG = "ReceiverHelper";        /**       * 解析插件Apk文件中的 <receiver>, 并存储起来       *       * @param apkFile       * @throws Exception       */      public static void preLoadReceiver(Context context, File apkFile) {          // 首先调用parsePackage获取到apk对象对应的Package对象          Object packageParser = RefInvoke.createObject("android.content.pm.PackageParser");          Class[] p1 = {File.class, int.class};          Object[] v1 = {apkFile, PackageManager.GET_RECEIVERS};          Object packageObj = RefInvoke.invokeInstanceMethod(packageParser, "parsePackage", p1, v1);            String packageName = (String)RefInvoke.getFieldObject(packageObj, "packageName");            // 读取Package对象里面的receivers字段,注意这是一个 List<Activity> (没错,底层把<receiver>当作<activity>处理)          // 接下来要做的就是根据这个List<Activity> 获取到Receiver对应的 ActivityInfo (依然是把receiver信息用activity处理了)          List receivers = (List) RefInvoke.getFieldObject(packageObj, "receivers");            try {              for (Object receiver : receivers) {                  Bundle metadata = (Bundle)RefInvoke.getFieldObject(                          "android.content.pm.PackageParser$Component", receiver, "metaData");                  String oldAction = metadata.getString("oldAction");                    // 解析出 receiver以及对应的 intentFilter                  List<? extends IntentFilter> filters = (List<? extends IntentFilter>) RefInvoke.getFieldObject(                          "android.content.pm.PackageParser$Component", receiver, "intents");                    // 把解析出来的每一个静态Receiver都注册为动态的                  for (IntentFilter intentFilter : filters) {                      ActivityInfo receiverInfo = (ActivityInfo) RefInvoke.getFieldObject(receiver, "info");                      BroadcastReceiver broadcastReceiver = (BroadcastReceiver) RefInvoke.createObject(receiverInfo.name);                      context.registerReceiver(broadcastReceiver, intentFilter);                        String newAction = intentFilter.getAction(0);                      ReceiverManager.pluginReceiverMappings.put(oldAction, newAction);                  }              }          } catch (Exception e) {              e.printStackTrace();          }      }  }  public class StubReceiver extends BroadcastReceiver {      public StubReceiver() {      }        @Override      public void onReceive(Context context, Intent intent) {          String newAction = intent.getAction();          if(ReceiverManager.pluginReceiverMappings.containsKey(newAction)) {              String oldAction = ReceiverManager.pluginReceiverMappings.get(newAction);              context.sendBroadcast(new Intent(oldAction));          }      }  }

缺点是要为StubReceiver配置几百个Action,无法避免

–摘自《android插件化开发指南》