VirtualAPK的使用及插件加载

VirtualAPK的使用

VirtualAPK的使用还是蛮简单的,根据提供的文档一步一步来就可以了,但是其中有一点需要注意,那就是plugin的打包。plugin是且必须是一个apk文件,但是我们不能像正常打包流程那样进行打包,否则会抛出java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity这个异常。我们需要执行命令行./gradlew clean assemblePlugin或者在AS右边的Gradle里点击assemblePlugin来进行打包。这样就可以使用VirtualApk来加载Plugin了。

Plugin的加载

LoadedPlugin中实现了Plugin的加载,主要代码如下:

    public LoadedPlugin(PluginManager pluginManager, Context context, File apk) throws Exception {
        this.mPluginManager = pluginManager;
        this.mHostContext = context;//主工程的Context
        this.mLocation = apk.getAbsolutePath();//插件的路径
        //解析Plugin文件(Plugin必须是一个apk文件)
        this.mPackage = PackageParserCompat.parsePackage(context, apk, PackageParser.PARSE_MUST_BE_APK);
        this.mPackage.applicationInfo.metaData = this.mPackage.mAppMetaData;
        this.mPackageInfo = new PackageInfo();//存储plugin的包信息
        this.mPackageInfo.applicationInfo = this.mPackage.applicationInfo;
        this.mPackageInfo.applicationInfo.sourceDir = apk.getAbsolutePath();

        //签名信息
        if (Build.VERSION.SDK_INT >= 28
            || (Build.VERSION.SDK_INT == 27 && Build.VERSION.PREVIEW_SDK_INT != 0)) { // Android P Preview
            try {
                this.mPackageInfo.signatures = this.mPackage.mSigningDetails.signatures;
            } catch (Throwable e) {
                PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);
                this.mPackageInfo.signatures = info.signatures;
            }
        } else {
            this.mPackageInfo.signatures = this.mPackage.mSignatures;
        }
        
        this.mPackageInfo.packageName = this.mPackage.packageName;//包名
        if (pluginManager.getLoadedPlugin(mPackageInfo.packageName) != null) {//判断plugin是否已经加载
            throw new RuntimeException("plugin has already been loaded : " + mPackageInfo.packageName);
        }
        this.mPackageInfo.versionCode = this.mPackage.mVersionCode;
        this.mPackageInfo.versionName = this.mPackage.mVersionName;
        this.mPackageInfo.permissions = new PermissionInfo[0];
        this.mPackageManager = createPluginPackageManager();//创建plugin包管理
        this.mPluginContext = createPluginContext(null);//创建插件的context
        this.mNativeLibDir = getDir(context, Constants.NATIVE_DIR);
        this.mPackage.applicationInfo.nativeLibraryDir = this.mNativeLibDir.getAbsolutePath();
        this.mResources = createResources(context, getPackageName(), apk);//创建Resources
        //创建一个ClassLoader,加载plugin中的class
        this.mClassLoader = createClassLoader(context, apk, this.mNativeLibDir, context.getClassLoader());

        tryToCopyNativeLib(apk);//copy插件的so文件到主工程

        // Cache instrumentations
        Map<ComponentName, InstrumentationInfo> instrumentations = new HashMap<ComponentName, InstrumentationInfo>();
        for (PackageParser.Instrumentation instrumentation : this.mPackage.instrumentation) {
            instrumentations.put(instrumentation.getComponentName(), instrumentation.info);
        }
        this.mInstrumentationInfos = Collections.unmodifiableMap(instrumentations);//将mInstrumentationInfos改为只读
        this.mPackageInfo.instrumentation = instrumentations.values().toArray(new InstrumentationInfo[instrumentations.size()]);

        // Cache activities
        Map<ComponentName, ActivityInfo> activityInfos = new HashMap<ComponentName, ActivityInfo>();
        for (PackageParser.Activity activity : this.mPackage.activities) {
            activity.info.metaData = activity.metaData;
            activityInfos.put(activity.getComponentName(), activity.info);
        }
        this.mActivityInfos = Collections.unmodifiableMap(activityInfos);//将mActivityInfos改为只读
        this.mPackageInfo.activities = activityInfos.values().toArray(new ActivityInfo[activityInfos.size()]);

        // Cache services
        Map<ComponentName, ServiceInfo> serviceInfos = new HashMap<ComponentName, ServiceInfo>();
        for (PackageParser.Service service : this.mPackage.services) {
            serviceInfos.put(service.getComponentName(), service.info);
        }
        this.mServiceInfos = Collections.unmodifiableMap(serviceInfos);//将mServiceInfos改为只读
        this.mPackageInfo.services = serviceInfos.values().toArray(new ServiceInfo[serviceInfos.size()]);

        // Cache providers
        Map<String, ProviderInfo> providers = new HashMap<String, ProviderInfo>();
        Map<ComponentName, ProviderInfo> providerInfos = new HashMap<ComponentName, ProviderInfo>();
        for (PackageParser.Provider provider : this.mPackage.providers) {
            providers.put(provider.info.authority, provider.info);
            providerInfos.put(provider.getComponentName(), provider.info);
        }
        this.mProviders = Collections.unmodifiableMap(providers);//将mProviders改为只读
        this.mProviderInfos = Collections.unmodifiableMap(providerInfos);
        this.mPackageInfo.providers = providerInfos.values().toArray(new ProviderInfo[providerInfos.size()]);

        // 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实例
            BroadcastReceiver br = BroadcastReceiver.class.cast(getClassLoader().loadClass(receiver.getComponentName().getClassName()).newInstance());
            for (PackageParser.ActivityIntentInfo aii : receiver.intents) {
                this.mHostContext.registerReceiver(br, aii);//将plugin中的静态BroadcastReceiver转动态注册
            }
        }
        this.mReceiverInfos = Collections.unmodifiableMap(receivers);//将mReceiverInfos改为只读
        this.mPackageInfo.receivers = receivers.values().toArray(new ActivityInfo[receivers.size()]);
    
        // try to invoke plugin's application
        invokeApplication();//创建plugin的Application对象并调用onCreate方法
    }

createResources的主要实现如下

    protected Resources createResources(Context context, String packageName, File apk) throws Exception {
        if (Constants.COMBINE_RESOURCES) {//将plugin的资源与主工程的资源合并
            return ResourcesManager.createResources(context, packageName, apk);
        } else {//为plugin创建单独的资源管理器
            Resources hostResources = context.getResources();
            AssetManager assetManager = createAssetManager(context, apk);
            return new Resources(assetManager, hostResources.getDisplayMetrics(), hostResources.getConfiguration());
        }
    }

 合并资源的代码主要在ResourcesManager中,主要是我们新创建一个包含主项目及plugin项目资源的Resources对象,然后通过反射进行替换。在这里最主要的问题时由于国内对Android系统进行了修改,所以导致兼容性有问题。
createClassLoader主要实现如下

    protected ClassLoader createClassLoader(Context context, File apk, File libsDir, ClassLoader parent) throws Exception {
        File dexOutputDir = getDir(context, Constants.OPTIMIZE_DIR);
        String dexOutputPath = dexOutputDir.getAbsolutePath();
        DexClassLoader loader = new DexClassLoader(apk.getAbsolutePath(), dexOutputPath, libsDir.getAbsolutePath(), parent);

        if (Constants.COMBINE_CLASSLOADER) {//如果合并,则将plugin中的dex文件插入到主工程的dex文件之后
            DexUtil.insertDex(loader, parent, libsDir);
        }

        return loader;
    }

 主要就是为Plugin创建一个ClassLoader来加载dex文件,如果Constants.COMBINE_CLASSLOADERtrue,则将plugin中的dex文件插入到主项目的dex列表中,代码如下


    public static void insertDex(DexClassLoader dexClassLoader, ClassLoader baseClassLoader, File nativeLibsDir) throws Exception {
        Object baseDexElements = getDexElements(getPathList(baseClassLoader));//获取主项目的dex列表
        Object newDexElements = getDexElements(getPathList(dexClassLoader));//获取plugin的dex文件列表
        Object allDexElements = combineArray(baseDexElements, newDexElements);//合并主工程与plugin的dex文件,plugin的dex文件在主工程后面
        Object pathList = getPathList(baseClassLoader);
        Reflector.with(pathList).field("dexElements").set(allDexElements);//将合并后的dex列表设置给主工程

        insertNativeLibrary(dexClassLoader, baseClassLoader, nativeLibsDir);//
    }

 最后就是通过invokeApplication去创建plugin中的Application并且调用其生命周期。其实plugin的加载流程跟Android安装一个apk的流程有点类似的。可以说是简化版的安装apk。

Android插件化快速入门与实例解析(VirtualApk)

猜你喜欢

转载自blog.csdn.net/lmh_19941113/article/details/85048863