BroadcastReceiver插件化解决方案

2018/12/12 15:54
阅读数 34

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

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,无法避免

欢迎关注我的微信公众号:安卓圈

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部