DroidPlugin源码分析:插件的查找、解析与安装过程

了解插件化需要先了解通过Binder进行IPC以及Hook这两块儿内容,推荐 维术大神的插件化系列文章,如果想看插件化的具体实现细节原理可以说这个系列的文章必看,当然还要对framework层的源码有一定的了解,比如对四大组件的生命周期过程有一定的了解。因为插件化技术涉及到的不仅仅是一个框架几个类,更多的是对Android Framework层源码的代理反射,只有在了解系统源码的情况下才能找到有效的hook点,才能达到期满AMS/PMS等系统服务,因此,分析DroidPlugin框架的源码建立在对源码有一定的了解的基础上。

在分析源码之前,先对下边几个重要的类进行一下简要说明:

  1. PluginHelper:外观模式,在Application的onCreate方法中进行插件管理(PluginManager)、进程管理(PluginProcessManager)等模块的初始化。
  2. IPluginManager.aidl:aidl文件,定义了提供操作插件的方法。比如安装、删除插件,解析插件中的四大组件信息、获取插件权限等。
  3. PluginManager: 插件管理服务客户端(client)。持有服务端的Binder代理IPluginManager。宿主通过该类中持有的代理间接调用server端提供的方法。
  4. PluginManagerService:一个运行在后台的service,向客户端提供IPluginManager.Stub实现类,处理客户端的IPC请求。
  5. PackageParser:插件解析器,用来解析一个插件apk中的信息。策略模式(抽象类),针对不同sdk版本提供了不同的实现,最终是通过反射调用系统 android.content.pm 包下的PackageParser来获取插件中的所有信息。如签名文件,dex路径,清单文件信息(包括声明的四大组件,权限,版本号)等。【其实获取到一个插件apk对应的packageParser就可以获取到基本所有的信息】,在首次装载完成之后,会使用一个Map进行缓存。判断一个插件是否已经装在也是根据是否命中缓存来判断。

先阐述一下DroidPlugin中涉及的Binder通信:
首先在app启动时,会开启并连接一个服务PluginManagerService,该service向客户端(PluginManager) 提供一个实现了IPluginManager.Stub的Binder代理对象,在该对象中实现了对插件apk的各种操作。服务连接成功之后,就可以在宿主app中通过客户端PluginManager中的binder代理对象来实现远程调用,从而实现插件的管理。

开始分析:

一、DroidPlugin的集成
  • 方式一:清单文件中直接指定
        android:name="com.morgoo.droidplugin.PluginApplication"
  • 方式二:自定义Application:
public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        PluginHelper.getInstance().applicationOnCreate(getBaseContext());
    }

    @Override
    protected void attachBaseContext(Context base) {
        PluginHelper.getInstance().applicationAttachBaseContext(base);
        super.attachBaseContext(base);
    }
}

集成完毕。

二、扫描并解析某个路径中的插件
在扫描本地插件文件之前,首先会进行判断:
    if (PluginManager.getInstance().isConnected()) {
            startLoad();
    }

    public boolean isConnected() {
        return mHostContext != null && mPluginManager != null;
    }

其实判断的是宿主application已经初始化并且连接到了PluginManagerService并获取到了binder代理对象。

扫描

也很简单:

    File file = Environment.getExternalStorageDirectory();

    List apks = new ArrayList(10);
    File[] files = file.listFiles();
    if (files != null) {
        for (File apk : files) {
        if (apk.exists() && apk.getPath().toLowerCase().endsWith(".apk")) {
            apks.add(apk);
        }
        }
    }

就是扫描指定文件夹下的所有.apk文件。此处示例为sd卡根目录。

解析

接下来就是接下解析扫描到的插件信息:

    PackageManager pm = getActivity().getPackageManager();
    for (final File apk : apks) {
        try {
        if (apk.exists() && apk.getPath().toLowerCase().endsWith(".apk")) {
                    //调用系统方法来解析一个压缩文件中的安装包信息
            final PackageInfo info = pm.getPackageArchiveInfo(apk.getPath(), 0);
            if (info != null && isViewCreated) {  //此处是展示插件的图标,名称,版本号信息。
            try {
                handler.post(new Runnable() {
                @Override
                public void run() {
                    adapter.add(new ApkItem(getActivity(), info, apk.getPath()));
                }
                });
            } catch (Exception e) {
            }
            }
        }
        } catch (Exception e) {
        }
    }

通过调用系统api,来解析一个安装包对应的信息。
结果如图:


DroidPlugin源码分析:插件的查找、解析与安装过程_第1张图片
image.png

解析完毕。

三、安装插件

首次安装:

    if (mPluginCache.containsKey(info.packageName)) {   //已经安装
        return PackageManagerCompat.INSTALL_FAILED_ALREADY_EXISTS;
    } else {
        forceStopPackage(info.packageName);
        new File(apkfile).delete();     
        Utils.copyFile(filepath, apkfile);      //将要安装的插件apk包copy到/data/user/0/com.zhu.droidplugindemo/Plugin/com.zhu.loginmodule/apk/base-1.apk
        PluginPackageParser parser = new PluginPackageParser(mContext, new File(apkfile));  //解析插件包
        parser.collectCertificates(0);  //收集签名信息
        PackageInfo pkgInfo = parser.getPackageInfo(PackageManager.GET_PERMISSIONS | PackageManager.GET_SIGNATURES);
        if (pkgInfo != null && pkgInfo.requestedPermissions != null && pkgInfo.requestedPermissions.length > 0) {
        for (String requestedPermission : pkgInfo.requestedPermissions) {
            boolean b = false;
            try {
            b = pm.getPermissionInfo(requestedPermission, 0) != null;
            } catch (NameNotFoundException e) {
            }
            if (!mHostRequestedPermission.contains(requestedPermission) && b) {
            Log.w(TAG, "No Permission %s", requestedPermission);
//                                new File(apkfile).delete();
//                                return PluginManager.INSTALL_FAILED_NO_REQUESTEDPERMISSION;
            }
        }
        }
        saveSignatures(pkgInfo);    //保存签名信息到/data/user/0/pkgName/Plugin/Signature/
//                    if (pkgInfo.reqFeatures != null && pkgInfo.reqFeatures.length > 0) {
//                        for (FeatureInfo reqFeature : pkgInfo.reqFeatures) {
//                            Log.e(TAG, "reqFeature name=%s,flags=%s,glesVersion=%s", reqFeature.name, reqFeature.flags, reqFeature.getGlEsVersion());
//                        }
//                    }
            //copy本地lib包
        if (copyNativeLibs(mContext, apkfile, parser.getApplicationInfo(0)) < 0) {
        new File(apkfile).delete();
        return PackageManagerCompat.INSTALL_FAILED_NOT_SUPPORT_ABI;
        }

        dexOpt(mContext, apkfile, parser);
        mPluginCache.put(parser.getPackageName(), parser);
        mActivityManagerService.onPkgInstalled(mPluginCache, parser, parser.getPackageName());
        sendInstalledBroadcast(info.packageName);    //发送广播
        return PackageManagerCompat.INSTALL_SUCCEEDED;
    }

方法说明:

  1. 首先将要安装的插件apk包copy到/data/user/0/com.zhu.droidplugindemo/Plugin/com.zhu.loginmodule/apk/base-1.apk。
  2. 获取到对应版本的包解析器:PackageParser。
  3. 获取插件的签名信息。
  4. 插件的权限检查,如果插件中存在宿主未声明的权限,则返回对应的信息【插件化开发的一个弊端就是要事先将所有的权限都声明到宿主中去】
  5. 保存签名信息到/data/user/0/com.zhu.droidplugindemo/Plugin/Signature/目录下


    image.png
  6. 复制lib包下的native库到指定目录


    image.png
  7. 将该插件的解析器packageParser加入到缓存中。
  8. 通知宿主安装完成,发送ACTION_PACKAGE_ADDED广播。

至此,插件的首次安装过程完毕。
更新插件的过程和上边大同小异,只不过在开始安装之前清除了插件/data/data/pkgName/目录下对应的缓存目录:

if (mPluginCache.containsKey(info.packageName)) {
    deleteApplicationCacheFiles(info.packageName, null);
}

ok,到此为止,插件已经算是安装完毕。

???这就完了?? 不就是解析了插件包并复制出了几个文件到指定目录吗?

Ofcourse not!此处只能说是识别出了插件,但是还并没有安装插件,还不能通过startActivity来打开插件


往下继续阅读之前,建议先仔细阅读了解 Android 插件化原理解析——Activity生命周期管理 一文中的内容。因为涉及到的点比较多,还包括一些源码,此处就不再赘述,下边的内容假设已经了解了Activity启动过程以及Hook技术。

Hook系统服务

1. 安装Hook

在宿主的Application的OnCreate方法中,会首先调用这么一行代码:

    PluginProcessManager.installHook(baseContext);

该方法最终会调用到HookFactory中的installHook方法:

    public final void installHook(Context context, ClassLoader classLoader) throws Throwable {
        if (ProcessUtils.isMainProcess(context)) {
            installHook(new IActivityManagerHook(context), classLoader);
            installHook(new IPackageManagerHook(context), classLoader);
        } else {
            installHook(new IClipboardBinderHook(context), classLoader);
            //for ISearchManager
            installHook(new ISearchManagerBinderHook(context), classLoader);
            //for INotificationManager
            installHook(new INotificationManagerBinderHook(context), classLoader);
            installHook(new IMountServiceBinderHook(context), classLoader);
            installHook(new IAudioServiceBinderHook(context), classLoader);
            installHook(new IContentServiceBinderHook(context), classLoader);
            installHook(new IWindowManagerBinderHook(context), classLoader);
            if (VERSION.SDK_INT > VERSION_CODES.LOLLIPOP_MR1) {
                installHook(new IGraphicsStatsBinderHook(context), classLoader);
            }
//        if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
//            installHook(new WebViewFactoryProviderHook(context), classLoader);
//        }
            if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
                installHook(new IMediaRouterServiceBinderHook(context), classLoader);
            }
            if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
                installHook(new ISessionManagerBinderHook(context), classLoader);
            }
            if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2) {
                installHook(new IWifiManagerBinderHook(context), classLoader);
            }

            if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2) {
                installHook(new IInputMethodManagerBinderHook(context), classLoader);
            }
            if (VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
                installHook(new ILocationManagerBinderHook(context), classLoader);
            }

            if (VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
                installHook(new ITelephonyRegistryBinderHook(context), classLoader);
            }

            if (VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
                installHook(new ISubBinderHook(context), classLoader);
            }

            if (VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
                installHook(new IPhoneSubInfoBinderHook(context), classLoader);
            }

            if (VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
                installHook(new ITelephonyBinderHook(context), classLoader);
            }

            if (VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
                installHook(new ISmsBinderHook(context), classLoader);
            }

            if (VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
                installHook(new IMmsBinderHook(context), classLoader);
            }

            if (VERSION.SDK_INT >= VERSION_CODES.M) {
                installHook(new IAppOpsServiceBinderHook(context), classLoader);
            }
            installHook(new IActivityManagerHook(context), classLoader);
            installHook(new IPackageManagerHook(context), classLoader);
            installHook(new PluginCallbackHook(context), classLoader);
            installHook(new InstrumentationHook(context), classLoader);
            installHook(new LibCoreHook(context), classLoader);

            installHook(new SQLiteDatabaseHook(context), classLoader);

            installHook(new IDisplayManagerBinderHook(context), classLoader);
        }
    }

安装Hook:

    public void installHook(Hook hook, ClassLoader cl) {
        try {
            hook.onInstall(cl);
            synchronized (mHookList) {
                mHookList.add(hook);
            }
        } catch (Throwable throwable) {
            Log.e(TAG, "installHook %s error", throwable, hook);
        }
    }

ok,调用了每个Hook的onInstall方法。那么这个Hook是个什么东西呢?

Hook是什么?

Hook在该框架里边表现的是一个类,该类实现的功能就是Hook某个系统类。如果不是特别了解hook技术,请先阅读Hook机制之动态代理

Hook掉系统的好多服务

在连接到PluginManagerService之后,会在onServiceConnected中收到通知,PluginHelper中注册了一份(PluginManager中也注册了一份)。因此在连接到插件服务后,PluginHelper中做了如下操作:

    @Override
    public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
        PluginProcessManager.setHookEnable(true, true);
    }

从字面意思看,就是开启了Hook。

    PluginProcessManager.java
    public static void setHookEnable(boolean enable, boolean reinstallHook) {
        HookFactory.getInstance().setHookEnable(enable, reinstallHook);
    }

发现又调用了HookFactory中的方法。

    public void setHookEnable(boolean enable, boolean reinstallHook) {
        synchronized (mHookList) {
            for (Hook hook : mHookList) {
                hook.setEnable(enable, reinstallHook);
            }
        }
    }

在Hook工厂类中持有一个Hook列表,然后循环遍历每个Hook,并开启自身的hook。

其实说白了,开启hook就是找到需要hook掉系统某个服务或者api中的方法(hook点),然后针对该方法进行进行动态代理,通过反射来修改反射前传入的参数或者结果,从而达到修改参数或者修改返回结果的功能
DroidPlugin源码分析:插件的查找、解析与安装过程_第2张图片
Hook的子类

可以看到,基本上平时能接触到的系统服务都被动态代理了。之后通过宿主app进行系统api的一些调用,都会走入到相关动态代理的方法中。

下边以打开插件中的Activity为例,来揭开hook的面纱。
想要在宿主app中打开插件中的Activity首先主要需要解决两个问题:

  1. 绕过AMS限制。
  2. 加载插件中的类。

针对这两个问题的详细讲解可以参考:DroidPlugin总结

问题一的解决方法:

  1. 占坑,宿主清单文件中声明StubActivity。
public abstract class ActivityStub extends Activity {

    private static class SingleInstanceStub extends ActivityStub {
    }

    private static class SingleTaskStub extends ActivityStub {
    }

    private static class SingleTopStub extends ActivityStub {
    }

    private static class StandardStub extends ActivityStub {
    }

   public static class P00{

        public static class SingleInstance00 extends SingleInstanceStub {
        }

        public static class SingleTask00 extends SingleTaskStub {
        }

        public static class SingleTop00 extends SingleTopStub {
        }

        public static class SingleInstance01 extends SingleInstanceStub {
        }

        public static class SingleTask01 extends SingleTaskStub {
        }

        public static class SingleTop01 extends SingleTopStub {
        }

        public static class SingleInstance02 extends SingleInstanceStub {
        }

        public static class SingleTask02 extends SingleTaskStub {
        }

        public static class SingleTop02 extends SingleTopStub {
        }

        public static class SingleInstance03 extends SingleInstanceStub {
        }

        public static class SingleTask03 extends SingleTaskStub {
        }

        public static class SingleTop03 extends SingleTopStub {
        }

        public static class Standard00 extends StandardStub {
        }
    }
    //p1..p2...p3...
}
DroidPlugin源码分析:插件的查找、解析与安装过程_第3张图片
image.png

首先定义了各种启动模式以及多个进程中的Activity,然后在清单文件中声明这些Activity。

  1. 之后,获取插件启动Activity的Intent,在startActivity后交给AMS检查之前,将Intent中插件的信息替换为宿主的信息,并将旧的Inent作为extra保存到新的Intent中
    private static class startActivity extends ReplaceCallingPackageHookedMethodHandler {

        public startActivity(Context hostContext) {
            super(hostContext);
        }
        @Override
        protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable {
           if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
                     bRet = doReplaceIntentForStartActivityAPILow(args);
            } else {
                   bRet = doReplaceIntentForStartActivityAPIHigh(args);
            }
            return super.beforeInvoke(receiver, method, args);
        }
    }
        protected boolean doReplaceIntentForStartActivityAPILow(Object[] args) throws RemoteException {
            int intentOfArgIndex = findFirstIntentIndexInArgs(args);
            if (args != null && args.length > 1 && intentOfArgIndex >= 0) {
                Intent intent = (Intent) args[intentOfArgIndex];
                ActivityInfo activityInfo = resolveActivity(intent);
                if (activityInfo != null && isPackagePlugin(activityInfo.packageName)) {
                    ComponentName component = selectProxyActivity(intent);
                    if (component != null) {
                        Intent newIntent = new Intent();
                        newIntent.setComponent(component);
                        newIntent.putExtra(Env.EXTRA_TARGET_INTENT, intent);
                        newIntent.setFlags(intent.getFlags());
                        if (TextUtils.equals(mHostContext.getPackageName(), activityInfo.packageName)) {
                            newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                        }
                        args[intentOfArgIndex] = newIntent;
                    } else {
                        Log.w(TAG, "startActivity,replace selectProxyActivity fail");
                    }
                }
            }
            return true;
        }
}

在setHookEnable(true); 之后,任何通过startActivity打开插件Activity的调用都会通过设置动态代理走进该方法。
beforeInvoke是真正反射调用系统方法之前,是在一个抽象代理类中声明的方法,用来暴露给各个子类进行具体实现。对应的还有afterInvoke方法。
在该方法中,首先会通过ComponentName component = selectProxyActivity(intent);来选取宿主中声明的一个同样启动模式的StubActivity,然后将该宿主Activity的Component替换插件的Component从而绕过AMS的检查,因为如果打开清单文件中没有注册的Activity会抛出异常。
通过本次hook,就绕过了AMS的这项检查。

  1. 在AMS检查回来之后,新建Activity之前,再将其替换回来。此时通过反射创建的Activity就是插件中的Activity了,同时由于插件Activity持有与AMS通信的token,因此可以进行后续的生命周期回调及IPC。
    在startActivity之后,根据系统源码,会通过ApplicationThread来通知app来创建该Activity。因此需要在ApplicationThread接收到系统之前来将其再替换回来,不然新建的就不是插件中的Activity,而是宿主声明的StubActivity了。
    所以需要寻找合适的hook点,来将其进行替换。这个hook点就是ActivityThread中的H类。
    具体的Hook点:
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

通过反射给ActivityThread中的mH添加一个自定义的mCallback,之后从AMS向ActivityThread发送的任何消息都会先经过mCallback的handleMessage,然后再在mCallback.handleMessage中将替换过的Activity再替换回来。

public class PluginCallback implements Handler.Callback{
    @Override
    public boolean handleMessage(Message msg) {      
            if (msg.what == LAUNCH_ACTIVITY) {
                return handleLaunchActivity(msg);
            }
        if (mCallback != null) {
                return mCallback.handleMessage(msg);
            } else {
                return false;
            }
        } 
    }

       private boolean handleLaunchActivity(Message msg) {
            Object obj = msg.obj;
            Intent stubIntent = (Intent) FieldUtils.readField(obj, "intent");
            stubIntent.setExtrasClassLoader(mHostContext.getClassLoader());
            Intent targetIntent = stubIntent.getParcelableExtra(Env.EXTRA_TARGET_INTENT);
            if (targetIntent != null && !isShortcutProxyActivity(stubIntent)) {
                IPackageManagerHook.fixContextPackageManager(mHostContext);
                ComponentName targetComponentName = targetIntent.resolveActivity(mHostContext.getPackageManager());
                ActivityInfo targetActivityInfo = PluginManager.getInstance().getActivityInfo(targetComponentName, 0);
                if (targetActivityInfo != null) {

                    if (targetComponentName != null && targetComponentName.getClassName().startsWith(".")) {
                        targetIntent.setClassName(targetComponentName.getPackageName(), targetComponentName.getPackageName() + targetComponentName.getClassName());
                    }
                    //解析插件信息,生成插件的ClassLoader
                    PluginProcessManager.preLoadApk(mHostContext, targetActivityInfo);
                    ClassLoader pluginClassLoader = PluginProcessManager.getPluginClassLoader(targetComponentName.getPackageName());
                    setIntentClassLoader(targetIntent, pluginClassLoader);
                    setIntentClassLoader(stubIntent, pluginClassLoader);

                    boolean success = false;
                    try {
                        targetIntent.putExtra(Env.EXTRA_TARGET_INFO, targetActivityInfo);
                        if (stubActivityInfo != null) {
                            targetIntent.putExtra(Env.EXTRA_STUB_INFO, stubActivityInfo);
                        }
                        success = true;
                    } catch (Exception e) {
                        Log.e(TAG, "putExtra 1 fail", e);
                    }

                    if (!success) {
                        Intent newTargetIntent = new Intent();
                        newTargetIntent.setComponent(targetIntent.getComponent());
                        newTargetIntent.putExtra(Env.EXTRA_TARGET_INFO, targetActivityInfo);
                        if (stubActivityInfo != null) {
                            newTargetIntent.putExtra(Env.EXTRA_STUB_INFO, stubActivityInfo);
                        }
                        FieldUtils.writeDeclaredField(msg.obj, "intent", newTargetIntent);
                    } else {
                        FieldUtils.writeDeclaredField(msg.obj, "intent", targetIntent);
                    }
                    FieldUtils.writeDeclaredField(msg.obj, "activityInfo", targetActivityInfo);
                } else {
                }
            } else {
            }

        if (mCallback != null) {
            return mCallback.handleMessage(msg);
        } else {
            return false;
        }
    }
}

替换的代码如上。方法说明:

  1. 将startActivity时替换的宿主的Activity再替换回来。
  2. 解析插件并生成插件的ClassLoader,然后放入缓存。因为在替换回来之后马上就要通过反射创建插件的Activity了。上述代码:PluginProcessManager.preLoadApk(mHostContext, targetActivityInfo);的作用就是解析插件apk生成ApplicationInfo,然后构建LoadedApk,之后再生成插件的ClassLoader。【因为构建ClassLoader需要LoadedApk中的dexFile,resource等信息】

此时,在经过AMS检查之后会将StubActivity替换为插件的Activity。然后就会走ActivityThread.handleLaunchActivity中通过反射创建插件的Activity。

但是,我们还没有办法通过宿主加载插件中的类。因为通过宿主的类加载器肯定会抛出ClassNotFound异常。这是第二个问题。

问题二解决方法
DroidPlugin的解决方法:

  1. 通过PackageParser解析插件构建插件的ApplicationInfo。
  2. 根据构建的ApplicationInfo构建插件的LoadedApk。
  3. 通过反射将插件的LoadedApk存入ActivityThread中的mPackages缓存中。

该问题的实现也是在上边的PluginCallback中的handleLaunchActivity方法中。
只有一句代码:PluginProcessManager.preLoadApk(mHostContext, targetActivityInfo);
核心逻辑:

Object object = ActivityThreadCompat.currentActivityThread();
            if (object != null) {
                Object mPackagesObj = FieldUtils.readField(object, "mPackages");
                Object containsKeyObj = MethodUtils.invokeMethod(mPackagesObj, "containsKey", pluginInfo.packageName);
                if (containsKeyObj instanceof Boolean && !(Boolean) containsKeyObj) {
                    final Object loadedApk;
                    if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
                        loadedApk = MethodUtils.invokeMethod(object, "getPackageInfoNoCheck", pluginInfo.applicationInfo, CompatibilityInfoCompat.DEFAULT_COMPATIBILITY_INFO());
                    } else {
                        loadedApk = MethodUtils.invokeMethod(object, "getPackageInfoNoCheck", pluginInfo.applicationInfo);
                    }
                    sPluginLoadedApkCache.put(pluginInfo.packageName, loadedApk);

                /*添加ClassLoader LoadedApk.mClassLoader*/

                    String optimizedDirectory = PluginDirHelper.getPluginDalvikCacheDir(hostContext, pluginInfo.packageName);
                    String libraryPath = PluginDirHelper.getPluginNativeLibraryDir(hostContext, pluginInfo.packageName);
                    String apk = pluginInfo.applicationInfo.publicSourceDir;
                    if (TextUtils.isEmpty(apk)) {
                        pluginInfo.applicationInfo.publicSourceDir = PluginDirHelper.getPluginApkFile(hostContext, pluginInfo.packageName);
                        apk = pluginInfo.applicationInfo.publicSourceDir;
                    }
                    if (apk != null) {
                        ClassLoader classloader = null;
                        try {
                            classloader = new PluginClassLoader(apk, optimizedDirectory, libraryPath, hostContext.getClassLoader().getParent());
                        } catch (Exception e) {
                        }
                        if(classloader==null){
                            PluginDirHelper.cleanOptimizedDirectory(optimizedDirectory);
                            classloader = new PluginClassLoader(apk, optimizedDirectory, libraryPath, hostContext.getClassLoader().getParent());
                        }
                        synchronized (loadedApk) {
                            FieldUtils.writeDeclaredField(loadedApk, "mClassLoader", classloader);
                        }
                        sPluginClassLoaderCache.put(pluginInfo.packageName, classloader);
                        Thread.currentThread().setContextClassLoader(classloader);
                        found = true;
                    }
                    ProcessCompat.setArgV0(pluginInfo.processName);
                }
            }

方法说明:

  1. 获取到插件的LoadedApk对象。
  2. 构建插件的ClassLoader并反射赋值给LoadedApk
  3. 将缓存存入Map缓存集合中。

此时,每个插件都有自己的classLoader,就可以加载自己的类。而且通过一系列hook也完成了插件Activity的生命周期的回调。此时,插件中的Activity已经可以被正常的使用了。

总结

你可能感兴趣的:(DroidPlugin源码分析:插件的查找、解析与安装过程)