插件化的核心之处,一言以蔽之,就是插件中类和资源的加载,类通过构造插件对应的ClassLoader加载,而插件资源则是通过构建对应的Resources实现,Resources内部又是通过AssetManager实现,两者都要指定插件的路径才行。为什么说插件对应的ClassLoader和Resources?因为宿主中的ClassLoader和Resources是无法加载插件中的类和资源的。
在插件初始化的时候,VirtualApk会将每一个插件的所有信息解析到一个LoadedPlugin对象中,包括上边提到的ClassLoader和Resources,四大组件信息,包相关信息等等,这一过程可类比系统解析宿主apk的解析方式,拿到这个LoadedPlugin备用。
Activity
VirtualApk是如何启动一个插件中的activity的?简单来说就是通过预设占坑的activity,启动插件activity的时候通过启动占坑activity绕过AMS检查,最后将插件activity重新替换回来,达到启动的目的。实现方式是通过Hook系统的Instrumentation,在execStartActivity方法中保存插件信息,找到合适的占坑activity,构造一个新的Intent,绕过AMS检查后,在Instrumentation通过newActivity的时候将目标Activity替换回来,达到启动插件Activity的目的。在这个过程中插件ClassLoader和插件Resource起到了决定性作用。
Receiver
Receiver的实现比较简单,在加载插件的时候读区到了所有在AndroidManifest中静态注册的Receiver,然后进行动态注册
// Register broadcast receivers dynamically
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);
}
}
Service
Service插件化没有采用类似对多种Activity形式进行占坑的方式,而是在本地定义了一个LocalService,通过hookInactivityManager拦截到启动或者关闭Service的方法,如startService stopService bindService unBindService等等,都会去启动这个localService,然后在LocalService中进行分发,根据传入的数据确定需要启动的目标Service,然后通过插件类加载器从插件中加载到这个类并反射创建对象,反射调用Service的attach方法,传入我们拼接好的参数,然后执行service对象的onCreate即可,其他操作同理
switch (command) {
case EXTRA_COMMAND_START_SERVICE: {
......
service = (Service) plugin.getClassLoader().loadClass(component.getClassName()).newInstance();
Application app = plugin.getApplication();
IBinder token = appThread.asBinder();
Method attach = service.getClass().getMethod("attach", Context.class, ActivityThread.class, String.class, IBinder.class, Application.class, Object.class);
IActivityManager am = mPluginManager.getActivityManager();
attach.invoke(service, plugin.getPluginContext(), mainThread, component.getClassName(), token, app, am);
service.onCreate();
......
break;
}
case EXTRA_COMMAND_BIND_SERVICE: {
......
break;
}
case EXTRA_COMMAND_STOP_SERVICE: {
......
break;
}
case EXTRA_COMMAND_UNBIND_SERVICE: {
......
break;
}
}
ContentProvider
VirtualApk中ContentProvider的插件化也是通过代理转发的形式实现的,它分为从外部调用插件ContentProvider和在插件内部调用插件自身的ContentProvider两种,第二种情况在插件内部getContentResolver是通过PluginContext调用的,两者原理相同,这里以第一种情况为例说明。在插件外部调起插件ContentProvider需要先通过PluginContentResolver.wrapperUri将uri进行包装,然后getContentResolver().query的时候就会调起占位的RemoteContentProvider执行它的query方法,在这个方法中会找到我们需要启动的那个ContentProvider,找到后反射创建对象,调用ContentProvider的attachInfo方法拼接参数,然后调用这个ContentProvider的query方法进行查询,其他方法同理
网友评论