RePlugin 记录(三)Activity调用流程

通过Replugin 记录(二)插件启动过程,我们知道插件中的Activity被gradle修改为继承PluginActivity,其中startActivty被重写,通过Factory2.startActivity显式调用Replugin启动插件activity的方法

    public void startActivity(Intent intent) {
        //反射调用Factory2.startActivity
        if (RePluginInternal.startActivity(this, intent)) {
            // 这个地方不需要回调startActivityAfter,因为Factory2最终还是会回调回来,最终还是要走super.startActivity()
            return;
        }

        super.startActivity(intent);
    }

除此之外还可以通过RePlugin.startActivity()在宿主启动插件activity。这两个方法最终都会调用Factory类的startActivityWithNoInjectCN。

   /**
     * 内部接口,仅为Factory2.startActivity(context, intent) 和 RePlugin.startActivity方法而使用
     *
     * @param context  应用上下文或者Activity上下文
     * @param intent   Intent对象
     * @param plugin   插件名
     * @param activity 待启动的activity类名
     * @param process  是否在指定进程中启动
     * @return 插件机制层,是否成功,例如没有插件存在、没有合适的Activity坑
     * Added by Jiongxuan Zhang
     */
    public static final boolean startActivityWithNoInjectCN(Context context, Intent intent, String plugin, String activity, int process) {
        boolean result = sPluginManager.startActivity(context, intent, plugin, activity, process);

        RePlugin.getConfig().getEventCallbacks().onStartActivityCompleted(plugin, activity, result);
        return result;
    }

PluginCommImpl.startActivity

/**
     * 启动一个插件中的activity,如果插件不存在会触发下载界面
     * @param context 应用上下文或者Activity上下文
     * @param intent
     * @param plugin 插件名
     * @param activity 待启动的activity类名
     * @param process 是否在指定进程中启动
     * @return 插件机制层是否成功,例如没有插件存在、没有合适的Activity坑
     */
    public boolean startActivity(Context context, Intent intent, String plugin, String activity, int process) {
        if (LOG) {
            LogDebug.d(PLUGIN_TAG, "start activity: intent=" + intent + " plugin=" + plugin + " activity=" + activity + " process=" + process);
        }

        return mPluginMgr.mInternal.startActivity(context, intent, plugin, activity, process, true);
    }

在开始看startActivity之前,我们要理一下一般插件化启动插件Activity做的工作。
1、把要启动的目标Activity替换成注册在mainfest上的占坑Activity(其中涉及进程选择启动,坑位选择分配)
而替换的原因是系统在startActivity的过程中会根据intent去PackageManagerService查询我们要启动的activity是否已经在mainfest中注册了。(PluginLibraryInternalProxy.startActivity)
2、在startActivity流程从ActivityManagerService回到activity所在进程时的调用流程时,把坑位Activity替换成真正要启动的Activity。(RePluginClassLoader.loadClass 完成)
3、插件化意味着类是动态加载的,系统默认创建的ClassLoader只能加载Apk中的类即宿主中的类,所以需要对ClassLoader进行操作使得它能加载插件中的类。(记录一中的PatchClassLoaderUtils.patch完成)

更多插件化相关知识可以看术哥的这篇文章,讲的很好 http://weishu.me/2016/03/21/understand-plugin-framework-activity-management/ Android 插件化原理解析——Activity生命周期管理

PluginLibraryInternalProxy.startActivity

 /**
     * @hide 内部方法,插件框架使用
     * 启动一个插件中的activity,如果插件不存在会触发下载界面
     * @param context 应用上下文或者Activity上下文
     * @param intent
     * @param plugin 插件名
     * @param activity 待启动的activity类名
     * @param process 是否在指定进程中启动
     * @param download 下载
     * @return 插件机制层是否成功,例如没有插件存在、没有合适的Activity坑
     */
    public boolean startActivity(Context context, Intent intent, String plugin, String activity, int process, boolean download) {
     
        // 是否启动下载
        // 若插件不可用(不存在或版本不匹配),则直接弹出“下载插件”对话框
        // 因为已经打开UpdateActivity,故在这里返回True,告诉外界已经打开,无需处理
        if (download) {
            if (PluginTable.getPluginInfo(plugin) == null) {
                // 如果用户在下载即将完成时突然点按“取消”,则有可能出现插件已下载成功,但没有及时加载进来的情况
                // 因此我们会判断这种情况,如果是,则重新加载一次即可,反之则提示用户下载
                // 原因:“取消”会触发Task.release方法,最终调用mDownloadTask.destroy,导致“下载服务”的Receiver被注销,即使文件下载了也没有回调回来
                // NOTE isNeedToDownload方法会调用pluginDownloaded再次尝试加载
                // 以下两种情况需要下载插件:
                // 1、V5文件不存在(常见);
                // 2、V5文件非法(加载失败)
                if (isNeedToDownload(context, plugin)) {
                    return RePlugin.getConfig().getCallbacks().onPluginNotExistsForActivity(context, plugin, intent, process);
                }
            }
        }

        /* 检查是否是动态注册的类 */
        // 如果要启动的 Activity 是动态注册的类,则不使用坑位机制,而是直接动态类。
        // 所以如果动态类是四大组件,被替换的类需要在宿主中注册
        // 原因:宿主的某些动态注册的类不能运行在坑位中(如'桌面'插件的入口Activity)
        if (Factory2.isDynamicClass(plugin, activity)) {
            intent.putExtra(IPluginManager.KEY_COMPATIBLE, true);
            intent.setComponent(new ComponentName(IPC.getPackageName(), activity));
            context.startActivity(intent);
            return true;
        }

        // 如果插件状态出现问题,则每次弹此插件的Activity都应提示无法使用,或提示升级(如有新版)
        // Added by Jiongxuan Zhang
        if (PluginStatusController.getStatus(plugin) < PluginStatusController.STATUS_OK) {
            return RePlugin.getConfig().getCallbacks().onPluginNotExistsForActivity(context, plugin, intent, process);
        }

        // 若为首次加载插件,且是“大插件”,则应异步加载,同时弹窗提示“加载中”
        // Added by Jiongxuan Zhang
        if (!RePlugin.isPluginDexExtracted(plugin)) {
            PluginDesc pd = PluginDesc.get(plugin);
            if (pd != null && pd.isLarge()) {
                return RePlugin.getConfig().getCallbacks().onLoadLargePluginForActivity(context, plugin, intent, process);
            }
        }

        // 缓存打开前的Intent对象,里面将包括Action等内容
        Intent from = new Intent(intent);

        // 帮助填写打开前的Intent的ComponentName信息(如有。没有的情况如直接通过Action打开等)
        if (!TextUtils.isEmpty(plugin) && !TextUtils.isEmpty(activity)) {
            from.setComponent(new ComponentName(plugin, activity));
        }
        //根据给要跳转的actvity所在插件找到一个合适的坑位
        ComponentName cn = mPluginMgr.mLocal.loadPluginActivity(intent, plugin, activity, process);
    
        // 将Intent指向到“坑位”。这样:
        // from:插件原Intent
        // to:坑位Intent
        intent.setComponent(cn);
        //启动坑位Activity,在RePluginClassLoader.loadClass加载Activity的时候会把它换回真正要加载的Activity
        context.startActivity(intent);

        // 通知外界,已准备好要打开Activity了
        // 其中:from为要打开的插件的Intent,to为坑位Intent
        RePlugin.getConfig().getEventCallbacks().onPrepareStartPitActivity(context, from, intent);

        return true;
    }

PluginCommImpl.loadPluginActivity
插件的坑位分配

 public ComponentName loadPluginActivity(Intent intent, String plugin, String activity, int process) {

        ActivityInfo ai = null;
        String container = null;
        PluginBinderInfo info = new PluginBinderInfo(PluginBinderInfo.ACTIVITY_REQUEST);

        // 获取 ActivityInfo
       // 找到对应插件的activityInfo。前面在加载插件的时候会把mainfest的四大组件信息缓存下来
         ai = getActivityInfo(plugin, activity, intent);
         // 存储此 Activity 在插件 Manifest 中声明主题到 Intent,作为坑位选择的一个条件
         intent.putExtra(INTENT_KEY_THEME_ID, ai.theme);
         // 根据 activity 的 processName,选择进程 ID 标识
        // 在Loader.loadDex加载插件的时候调用adjustPluginProcess(mPackageInfo.applicationInfo);
       // 调整activity的目标进程。(根据组件的目标进程填写,判断是在UI进程,还是在常驻进程还是自定义进程
       // 其中自定义进程需要把进程映射成p0, p1, p2这几个指定的进程)
       // 注:这里即使是调整成常驻进程,在startPluginProcess 中无效,最后会由系统分配进程
         if (ai.processName != null) {
             process = PluginClientHelper.getProcessInt(ai.processName);
         }
         // 容器选择(启动目标进程),返回目标进程的一个binder客户端后续请求调用
         IPluginClient client = MP.startPluginProcess(plugin, process, info);
         // 目标进程远程分配坑位
         container = client.allocActivityContainer(plugin, process, ai.name, intent);   
       //指向在宿主mainfest中注册好的组件坑位了
        return new ComponentName(IPC.getPackageName(), container);
    }

PmHostSvc.startPluginProcess

 @Override
    public IPluginClient startPluginProcess(String plugin, int process, PluginBinderInfo info) throws RemoteException {
        synchronized (this) {
            return mPluginMgr.startPluginProcessLocked(plugin, process, info);
        }
    }

PmBase.startPluginProcessLocked

final IPluginClient startPluginProcessLocked(String plugin, int process, PluginBinderInfo info) {
        // 如果是activity,binder这类请求,没有指定进程则强制使用UI进程
        if (Constant.ENABLE_PLUGIN_ACTIVITY_AND_BINDER_RUN_IN_MAIN_UI_PROCESS) {
            if (info.request == PluginBinderInfo.ACTIVITY_REQUEST) {
                if (process == IPluginManager.PROCESS_AUTO) {
                    process = IPluginManager.PROCESS_UI;
                }
            }
            if (info.request == PluginBinderInfo.BINDER_REQUEST) {
                if (process == IPluginManager.PROCESS_AUTO) {
                    process = IPluginManager.PROCESS_UI;
                }
            }
        }

        //根据插件名称,指定进程从已创建的进程中获取,如果已存在且还存活则直接返回。
       //对比PluginProcessMain.allocProcess来看
        IPluginClient client = PluginProcessMain.probePluginClient(plugin, process, info);
        if (client != null) {
            return client;
        }

        int index = IPluginManager.PROCESS_AUTO;
        // 分配进程。三种情况,ui进程,自定义进程(需要在mainfest写meta-data
        // 将自定义的进程映射成p0,p1,p2),由replugin来分配stub进程
        //(不属于ui也不是自定义进程,不过看样子已经不用了,所以不考虑这种情况)
        index = PluginProcessMain.allocProcess(plugin, process);
        // 分配的坑位不属于UI、自定义进程和自动分配的进程,就返回。
        if (!(index == IPluginManager.PROCESS_UI
                || PluginProcessHost.isCustomPluginProcess(index)
                || PluginManager.isPluginProcess(index))) {
            return null;
        }

        //根据上面分配的进程index,组装成一个uri来启动预先在mainfest中埋好的provider,因为provider
       // 在mainfest指定了目标进程,从而间接达到开启进程的效果。
        boolean rc = PluginProviderStub.proxyStartPluginProcess(mContext, index);
        if (!rc) {
            return null;
        }

        // 再次获取
        client = PluginProcessMain.probePluginClient(plugin, process, info);
        if (client == null) {
            return null;
        }
        return client;
    }

自定义进程:


RePlugin 记录(三)Activity调用流程_第1张图片
image.png

PluginProcessPer.allocActivityContainer

 @Override
    public String allocActivityContainer(String plugin, int process, String target, Intent intent) throws RemoteException {
        // 一旦有分配,则进入监控状态(一是避免不退出的情况,二也是最重要的是避免现在就退出的情况)
        RePlugin.getConfig().getEventCallbacks().onPrepareAllocPitActivity(intent);
        String loadPlugin = null;
        //,省略了process的调整,因为后面bindActivity其实没有用到
       //  省略了plugin的判断从PluginCommImpl.loadPluginActivity过来的话因为插件的判断已经在前面判断过了 

        String container = bindActivity(loadPlugin, process, target, intent);
        return container;
    }

PluginProcessPer.bindActivity

final String bindActivity(String plugin, int process, String activity, Intent intent) {

        /* 获取插件对象 */
        Plugin p = mPluginMgr.loadAppPlugin(plugin);
        /* 获取 ActivityInfo */
        ActivityInfo ai = p.mLoader.mComponents.getActivity(activity);

        /* 获取 Container */
        String container;

        // 根据是自定义进程还是UI进程,取不同的坑位信息。逻辑是一样的,所以只看一个mACM.alloc就够了
        if (ai.processName.contains(PluginProcessHost.PROCESS_PLUGIN_SUFFIX2)) {
            String processTail = PluginProcessHost.processTail(ai.processName);
            container = mACM.alloc2(ai, plugin, activity, process, intent, processTail);
        } else {
            container = mACM.alloc(ai, plugin, activity, process, intent);
        }
        //检查 activity 是否存在。这个classLoader是PluginDexClassLoader,如果在插件找不到
       // 会尝试在宿主中找该activity
        c = p.mLoader.mClassLoader.loadClass(activity);

        return container;
    }

PluginContainers.alloc

final String alloc(ActivityInfo ai, String plugin, String activity, int process, Intent intent) {
        ActivityState state;
       String defaultPluginTaskAffinity = ai.applicationInfo.packageName;
       //省略了其他启动模式,这里是/* SingleTask, SingleTop, Standard */
        synchronized (mLock) {
              state = allocLocked(ai, mLaunchModeStates.getStates(ai.launchMode, ai.theme), plugin, activity, intent);
         }
        return state.container;
    }

PluginContainers.allocLocked

private final ActivityState allocLocked(ActivityInfo ai, HashMap map,
                                            String plugin, String activity, Intent intent) {
        // 这里就贴下代码,原本的注释也够清楚的了,state.occupy(plugin, activity)将坑位和插件目标activity关联起来
        // 坑和状态的 map 为空
        if (map == null) {
            return null;
        }

        // 首先找上一个活的,或者已经注册的,避免多个坑到同一个activity的映射
        for (ActivityState state : map.values()) {
            if (state.isTarget(plugin, activity)) {
                return state;
            }
        }

        // 新分配:找空白的,第一个
        for (ActivityState state : map.values()) {
            if (state.state == STATE_NONE) {
                state.occupy(plugin, activity);
                return state;
            }
        }

        ActivityState found;

        // 重用:则找最老的那个
        found = null;
        for (ActivityState state : map.values()) {
            if (!state.hasRef()) {
                if (found == null) {
                    found = state;
                } else if (state.timestamp < found.timestamp) {
                    found = state;
                }
            }
        }
        if (found != null) {
            found.occupy(plugin, activity);
            return found;
        }

        // 强挤:最后一招,挤掉:最老的那个
        found = null;
        for (ActivityState state : map.values()) {
            if (found == null) {
                found = state;
            } else if (state.timestamp < found.timestamp) {
                found = state;
            }
        }
        if (found != null) {
            found.finishRefs();
            found.occupy(plugin, activity);
            return found;
        }
        // never reach here
        return null;
    }

上面进入startActivity后,最后系统调回ActivityThread.performLaunchActivity来创建Activity实例

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {

        if (r.packageInfo == null) {
            //获取LoadedApk,此时LoadedApk中的classLoader已经是RePluginClassLoader了
           // 看记录一的流程PatchClassLoaderUtils.patch方法
            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                    Context.CONTEXT_INCLUDE_CODE);
        }
        //创建context的时候把classLoader设置为r.packageInfo.getClassLoader();
        ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;
        java.lang.ClassLoader cl = appContext.getClassLoader();
        //最终调用cl.loadClass(className).newInstance()
        activity = mInstrumentation.newActivity(
                cl, component.getClassName(), r.intent);
        r.intent.setExtrasClassLoader(cl);
        if (r.state != null) {
            r.state.setClassLoader(cl);
        }
     
}

RePluginClassLoader.loadClass

@Override
    protected Class loadClass(String className, boolean resolve) throws ClassNotFoundException {
        Class c = null;
        //调用PmBase.loadClass 对应插件classLoader加载类或者是加载动态注册类
        c = PMF.loadClass(className, resolve);
        if (c != null) {
            return c;
        }
        //
        try {
            //如果找不到就用默认系统生成的PathClassLoader即加载宿主的类
            c = mOrig.loadClass(className);
            return c;
        } catch (Throwable e) {
            //
        }
        //
        return super.loadClass(className, resolve);
    }

PmBase.loadClass

 final Class loadClass(String className, boolean resolve) {
     
        //这里略去了contentprovider和service的情况
        // 判断是否坑位Activity
        if (mContainerActivities.contains(className)) {
            Class c = mClient.resolveActivityClass(className);
            if (c != null) {
                return c;
            }
            //找不到要加载的Activity返回DummyActivity,直接finish
            return DummyActivity.class;
        }
        // 插件定制表
        DynamicClass dc = mDynamicClasses.get(className);
        if (dc != null) {
           // 省略了加载失败的处理,其实也挺清晰的就是插件对应的classLoader去加载类
            Plugin p = loadAppPlugin(dc.plugin);
           if (p != null) {
                try {
                    Class cls = p.getClassLoader().loadClass(dc.className);
                    return cls;
                } catch (Throwable e) {
                }
            } 
            //返回一个关闭自己的组件
            // return dummy class
            if ("activity".equals(dc.classType)) {
                return DummyActivity.class;
            } else if ("service".equals(dc.classType)) {
                return DummyService.class;
            } else if ("provider".equals(dc.classType)) {
                return DummyProvider.class;
            }
            //如果不是组件,返回一个加载失败用的类
            return dc.defClass;
        }

        //加载默认插件的类
        return loadDefaultClass(className);
    }

PluginProcessPer.resolveActivityClass

 final Class resolveActivityClass(String container) {
        String plugin = null;
        String activity = null;

        // 根据container找之前bindActivity登记的ActivityState从而找到对应插件和activity,如果找不到,则用forward activity
        PluginContainers.ActivityState state = mACM.lookupByContainer(container);
        if (state == null) {
            //ForwardActivity作用是当坑位出现丢失或错乱,则通过读取Intent.Category来做个中转,再尝试启动一次
            return ForwardActivity.class;
        }
        plugin = state.plugin;
        activity = state.activity;

        Plugin p = mPluginMgr.loadAppPlugin(plugin);
        ClassLoader cl = p.getClassLoader();
        
        Class c = null;
        try {
            //对应插件来加载activity类
            c = cl.loadClass(activity);
        } catch (Throwable e) {
            
        }
        return c;
    }

参考:
Android 插件化原理解析——Activity生命周期管理:
http://weishu.me/2016/03/21/understand-plugin-framework-activity-management/
RePlugin中如何打开插件中的自定义进程Activity:https://mp.weixin.qq.com/s/IpNcyTjML16og4LrxjxFmQ

你可能感兴趣的:(RePlugin 记录(三)Activity调用流程)