一、说明
Android插件化相关文章是根据包建强大佬的《Android插件开发指南》书籍学习而来,只是后续的代码部分均是根据Android P源码所实现。想要了解Activity插件化的实现方式可直接跳转到Activity篇,Service插件化实现方式可直接跳转到Service篇。代码实现也上传到了github。
二、广播的注册方式
在Android中广播分为动态注册和静态注册两种方式。
1、动态注册
所谓动态注册,简单来说就是我们在通过Java代码实现广播的注册,当注册这个广播的进程死亡了之后,那么该广播随之被取消注册。比如我们通过Java代码动态注册了MyReceiver广播,而该广播则可接收IntentFilter中所携带的三个Action。
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.andorid.test.ACTION");
intentFilter.addAction("com.andorid.test.ACTION1");
intentFilter.addAction("com.andorid.test.ACTION2");
context.registerReceiver(new MyReceiver(), intentFilter);
复制代码
2、静态注册
&ems;所谓静态注册,简单来说就是在AndroidManifest.xml文件中对广播就进行注册。而通过该种方式注册的广播即使应用进程没有存活应用也能够接收到对应的广播,这也是实现拉活的手段之一。比如我们在AndroidManifest.xml中注册能够接收应用安装、卸载广播。
<receiver
android:name=".MyReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REMOVED" />
<action android:name="android.intent.action.PACKAGE_REPLACED" />
<data android:scheme="package" />
</intent-filter>
</receiver>
复制代码
对于动态注册的广播,我们直接创建了广播接收器对象并注册到框架中,因此对于插件中动态注册的广播我们并不需要做太多的处理。唯一需要处理的就是静态注册的广播接收器。
三、静态注册广播插件化处理
首先我们回忆一下在实现Activity与Service插件化之前我们都会在宿主中预埋一个对应的Activity或者Service用于欺骗AMS;对于这里的静态广播插件化处理也同样不例外需要在宿主中预埋一个Receiver,只是该Receiver的作用不再是欺骗AMS,而单纯是在接收到对应Action的广播之后再转发给插件静态注册的广播接收器。
1、插件中静态注册广播解析原理
在Android框架层中的PackageParser.java类存在一个叫做parsePackage的方法,它能够解析诸如存在在本地apk文件中的AndroidManifest.xml文件。但是该类并不直接提供给上层应用使用,所以我们需要通过反射的方式去使用该方法。方法声明如下:
public Package parsePackage(File packageFile, int flags, boolean useCaches)
throws PackageParserException {
Package parsed = useCaches ? getCachedResult(packageFile, flags) : null
//其它实现
......
return parsed;
}
复制代码
该方法返回的Package对象是PackageParser类的内部类。在该内部类中中存储着从AndroidManifest.xml文件中解析出来的数据,诸如:
public final ArrayList<Activity> activities = new ArrayList<Activity>(0);
public final ArrayList<Activity> receivers = new ArrayList<Activity>(0);
public final ArrayList<Provider> providers = new ArrayList<Provider>(0);
public final ArrayList<Service> services = new ArrayList<Service>(0);
复制代码
这里我们只需要关心存储着类对象Activity的receivers列表,注意此Activity非彼Activity,这里的Activity也是PackageParser类的一个内部类而已,其声明如下:
public final static class Activity extends Component<ActivityIntentInfo> implements Parcelable {
public final ActivityInfo info;
//其它实现
........
}
复制代码
该类又实现了Component类,该类不例外的也是PackageParser类的一个内部类,所以我们继续往上看。
public static abstract class Component<II extends IntentInfo> {
public final ArrayList<II> intents;
public final String className;
public Bundle metaData;
public Package owner;
/** The order of this component in relation to its peers */
public int order;
ComponentName componentName;
String componentShortName;
//其他方法实现
......
}
复制代码
注意对于静态注册在插件AndroidManifest.xml文件中的Receiver,宿主需要知道Receiver的className以及其对应注册的Action,这样在宿主中才能实现广播的中转;因此整个解析过程也就需要解析出广播的ClassName以及其注册的Action就OK了。
对于ClassName在Component已经出现,而对于Action我们则继续看Component类中的intents这个列表,该列表中所存储的对象对应的类集成至IntentInfo,因此我们还需要继续往上看才行。
public static abstract class IntentInfo extends IntentFilter {
public boolean hasDefault;
// 其他属性以及方法
....
}
复制代码
而该内部类又继承至类IntentFilter,这里IntentFilter则不再是PackageParser类的内部类了,它就是我们在动态注册Reciver所用到的IntentFilter;而在IntentFilter类中我们则能够直接通过其mActions属性拿到当前ClasName对应Receiver所静态注册的Action了。
2、插件中静态注册广播解析实现
接下来就是通过反射的方式调用PackageParser的parsePackage获取到PackageParser$Package对象,接着就是一步一步的对该对象进行解析了。对应源码如下:
public static void parsePackage(String apkPath) {
try {
//根据插件本地存储的文件地址生成对应的文件
File file = new File(apkPath);
if (!file.exists()) {
Log.i(TAG, "parse plugin receiver apk not exist");
return;
}
Log.i(TAG, "parse package path is " + apkPath);
//获取到PackageParser类对象
Class<?> cls = Class.forName("android.content.pm.PackageParser");
Object packageParserObj = RefInvoke.createObject(cls, null, null);
if (null == packageParserObj) {
Log.i(TAG, "parse package create packageParser object failed");
return;
}
//调用parsePackage方法
Object packageObj = RefInvoke.on(packageParserObj, "parsePackage", new Class[]{File.class, int.class})
.invoke(file, PackageManager.GET_RECEIVERS);
if (null == packageObj) {
Log.i(TAG, "parse package get packageObj failed");
return;
}
//获取PackageParser$Package对象中的receivers列表
List<Object> receivers = (List<Object>) RefInvoke.getFieldValue(RefInvoke.getField(packageObj.getClass(), "receivers"), packageObj);
if (null == receivers) {
Log.i(TAG, "parse package get receivers failed");
return;
}
//遍历receivers列表获取AndroidManifest.xml文件中所注册的Receiver信息
for (Object receiver : receivers) {
parseAction(receiver, apkPath);
}
} catch (Exception e) {
Log.i(TAG, "parse package failed " + e);
}
}
复制代码
上面代码就是通过反射的方式调用PackageParser的parsePackage方法,并遍历获取到的receivers列表。接下来就就是对receivers列表中的每个Receiver相关信息进行解析了。
private static void parseAction(Object receiver, String path) {
try {
//根据反射获取到PackageParser$Component对象中的intents列表
Class<?> cls = RefInvoke.getClass("android.content.pm.PackageParser$Component");
ArrayList<IntentFilter> intents = (ArrayList<IntentFilter>) RefInvoke.getFieldValue(RefInvoke.getField(cls, "intents"), receiver);
if (null == intents || 0 == intents.size()) {
return;
}
//获取该Receiver对应的ClassName
String clsName = (String) RefInvoke.getFieldValue(RefInvoke.getField(cls, "className"), receiver);
Log.i(TAG, "parseAction current receiver name is " + clsName);
//根据插件所在文件地址生成对应的ClassLoader并根据获取到的ClassName生成对应的对象
Object receiverObj = creatReceiverObj(clsName, path);
if (null == receiverObj) {
Log.i(TAG, "parseAction create receiver obj failed");
return;
}
//接着就是遍历该Receiver中所注册的Action并存储在内存中以便宿主在接收到对应Action的广播之后能够直接进行转发
for (IntentFilter intentFilter : intents) {
Class<?> intentFilterCls = RefInvoke.getClass("android.content.IntentFilter");
Log.i(TAG, "field: " + RefInvoke.getField(intentFilterCls, "mActions"));
List<String> actions = (List<String>) RefInvoke.getFieldValue(RefInvoke.getField(intentFilterCls, "mActions"), intentFilter);
for (String action : actions) {
registerActionToReceiver(action, receiverObj);
}
}
} catch (Exception e) {
Log.i(TAG, "parseAction failed " + e);
}
}
private static Object creatReceiverObj(String clsName, String path) {
try {
DexClassLoader dexClassLoader = DeHostDexClassloader.getInstance().getDexClassLoader(DePluginApplication.getContext(), path);
Class<?> cls = dexClassLoader.loadClass(clsName);
return RefInvoke.createObject(cls, null, null);
} catch (Exception e) {
Log.i(TAG, "createReceiverObj failed " + e.getCause());
}
return null;
}
private static void registerActionToReceiver(String action, Object receiverObj) {
List<Object> receiverObjs = mActionToReceiverMap.get(action);
if(null == receiverObjs){
receiverObjs = new ArrayList<>();
mActionToReceiverMap.put(action, receiverObjs);
}
receiverObjs.add(receiverObj);
}
复制代码
最后就是宿主中预埋的Receiver在接收到某个Action之后转发给插件中对应的广播接收器了。
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.i(TAG, "action is " + action);
List<Object> receiverObjs = PluginReceiverParseUtils.getReceiverObjByAction(action);
if (null != receiverObjs) {
for(Object obj : receiverObjs){
((BroadcastReceiver) obj).onReceive(context, intent);
}
Log.i(TAG, "translate receiver succ");
} else {
Log.i(TAG, "not exist plugin receiver");
}
}
复制代码
4、总结
相较于Activity和Service插件化来说,Receiver的插件化则稍显简单;我们只需要通过PackageParser类中的parsePackage方法解析出插件中的静态注册的广播,接着预埋在宿主中的广播接收器在接收到对应Action广播之后再直接转发给插件中的广播接收器就OK了;而不需要通过反射的方式与Activitythread类打交道。
当然如果不要求广播在应用进程非启动状态就能够接收到,除了上述通过转发的方式进行实现之外,我们还可以在宿主中直接动态注册从插件中解析出来的静态注册广播。
5、顺带说一句
对于ContentProvider插件化实现我们同样采用与Receiver相同的方式进行处理,因此就不做过多的赘述了,有需要的可以直接阅读书籍《Android插件开发指南》进行学习。