13.VirtualApk原理总结

插件化的核心之处,一言以蔽之,就是插件中类和资源的加载,类通过构造插件对应的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 receivers = new HashMap();
        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方法进行查询,其他方法同理

你可能感兴趣的:(13.VirtualApk原理总结)