RePlugin源码分析

Replugin源码解析

一、ClassLoader介绍

有3个,BootClassLoader,PathClassLoader,DexClassLoader

  1. BootClassLoader
    用来加载系统类,如Android中各种SystemManager各种Service类,对于所有应用来说加载到的都是同一个系统类,这个BootClassLoader是属于系统的,应用无法使用,防止应用随意加载系统级的类。
  2. PathClassLoader
    应用级的,是系统为每个应用分配的ClassLoader,用来加载已经安装到系统上的dex文件中的类,如果是一个未经过安装的apk,其中的dex文件中的类不可以被加载
  3. DexClassLoader
    应用级的,应用可以自己创建此ClassLoader的实例,去加载未经过安装的dex文件中的类。

todo继承关系

二、RePlugin中的ClassLoader

github地址 https://github.com/Qihoo360/RePlugin

主要有2个

1、RePluginClassLoader继承自PathClassLoader,宿主的ClassLoader,插件框架的核心之一。

2、PluginDexClassLoader继承自DexClassLoader,插件的DexClassLoader。

1 启动插件activity流程源码分析

RePlugin启动activity流程

先看构造方法,将系统的ClassLoader属性和方法都拷贝了过来

    public RePluginClassLoader(ClassLoader parent, ClassLoader orig) {

        // 由于PathClassLoader在初始化时会做一些Dir的处理,所以这里必须要传一些内容进来
        // 但我们最终不用它,而是拷贝所有的Fields
        super("", "", parent);
        mOrig = orig;//先将系统分配的ClassLoader缓存起来

        // 将原来宿主里的关键字段,拷贝到这个对象上,这样骗系统以为用的还是以前的东西(尤其是DexPathList)
        // 注意,这里用的是“浅拷贝”
        copyFromOriginal(orig);
        //通过反射将原始的ClassLoader中的方法反射出来
        initMethods(orig);
    }

RePluginClassLoader主要重写了loadClass方法

    @Override
    protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
        //
        Class<?> c = null;
        c = PMF.loadClass(className, resolve);
        if (c != null) {
            return c;
        }
        //
        try {
            c = mOrig.loadClass(className);
            // 只有开启“详细日志”才会输出,防止“刷屏”现象
            if (LogDebug.LOG && RePlugin.getConfig().isPrintDetailLog()) {
                LogDebug.d(TAG, "loadClass: load other class, cn=" + className);
            }
            return c;
        } catch (Throwable e) {
            //
        }
        //
        return super.loadClass(className, resolve);
    }

加载dex文件中的类,在加载时候每次都是通过PMF.loadClass,PMF是插件的管理类,每次先去插件中加载,优先使用插件中的类,如果没找到,就从宿主工程中用缓存下来的原始的ClassLoader加载。

接下来看看RePluginClassLoader是在什么时候对系统的ClassLoader进行的替换
在RePluginCallbacks中的createClassLoader方法创建RePluginClassLoader的实例

    /**
     * 创建【宿主用的】 RePluginClassLoader 对象以支持大多数插件化特征。默认为:RePluginClassLoader的实例
     * <p>
     * 子类可复写此方法,来创建自己的ClassLoader,做相应的事情(如Hook宿主的ClassLoader做一些特殊的事)
     *
     * @param parent   该ClassLoader的父亲,通常为BootClassLoader
     * @param original 宿主的原ClassLoader,通常为PathClassLoader
     * @return 支持插件化方案的ClassLoader对象,可直接返回RePluginClassLoader
     * @see RePluginClassLoader
     */
    public RePluginClassLoader createClassLoader(ClassLoader parent, ClassLoader original) {
        return new RePluginClassLoader(parent, original);
    }

接下来在PatchClassLoaderUtils调用,在patch方法中将application对象的PackageInfo对象的ClassLoader替换成自己的

    public static boolean patch(Application application) {
        try {
            // 获取Application的BaseContext (来自ContextWrapper)
            Context oBase = application.getBaseContext();
            ...

            // 通过反射获取mBase.mPackageInfo
            // 1. ApplicationContext - Android 2.1
            // 2. ContextImpl - Android 2.2 and higher
            // 3. AppContextImpl - Android 2.2 and higher
            Object oPackageInfo = ReflectUtils.readField(oBase, "mPackageInfo");
            ...

            // mPackageInfo的类型主要有两种:
            // 1. android.app.ActivityThread$PackageInfo - Android 2.1 - 2.3
            // 2. android.app.LoadedApk - Android 2.3.3 and higher
             
            // 通过反射获取mPackageInfo.mClassLoader
            ClassLoader oClassLoader = (ClassLoader) ReflectUtils.readField(oPackageInfo, "mClassLoader");
            ...
            //获取到一个新的ClassLoader。 外界可自定义ClassLoader的实现,但一定要基于RePluginClassLoader类。
            ClassLoader cl = RePlugin.getConfig().getCallbacks().createClassLoader(oClassLoader.getParent(), oClassLoader);

            // 将新的ClassLoader写入mPackageInfo.mClassLoader
            ReflectUtils.writeField(oPackageInfo, "mClassLoader", cl);

            // 设置线程上下文中的ClassLoader为RePluginClassLoader
            // 防止在个别Java库用到了Thread.currentThread().getContextClassLoader()时,“用了原来的PathClassLoader”,或为空指针
            Thread.currentThread().setContextClassLoader(cl);
 
        } 
        ...
    }

PMF类初始化init方法中调用了PatchClassLoaderUtils.patch 方法,然后在RePlugin类attachBaseContext方法中调用了PMF.init方法,然后在RePluginApplication类中attachBaseContext方法中调用RePlugin.App.attachBaseContext。我们的应用Application继承了RePluginApplication,所以在应用刚启动时候就会执行替换ClassLoader

这个自己的ClassLoader如何加载插件中的类
启动插件中Activity流程
先看RePlugin的startActivity方法

    /**
     * 开启一个插件的Activity <p>
     * 其中Intent的ComponentName的Key应为插件名(而不是包名),可使用createIntent方法来创建Intent对象
     *
     * @param context Context对象
     * @param intent  要打开Activity的Intent,其中ComponentName的Key必须为插件名
     * @return 插件Activity是否被成功打开?
     */
    public static boolean startActivity(Context context, Intent intent) {
        // TODO 先用旧的开启Activity方案,以后再优化
        ComponentName cn = intent.getComponent();
        if (cn == null) {
            // TODO 需要支持Action方案
            return false;
        }
        String plugin = cn.getPackageName();
        String cls = cn.getClassName();
        return Factory.startActivityWithNoInjectCN(context, intent, plugin, cls, IPluginManager.PROCESS_AUTO);
    }

接着看下Factory.startActivityWithNoInjectCN方法,核心是sPluginManager.startActivity方法,里面又调用了PluginLibraryInternalProxy的startActivity方法

    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再次尝试加载
                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);
            }
        }

        // WARNING:千万不要修改intent内容,尤其不要修改其ComponentName
        // 因为一旦分配坑位有误(或压根不是插件Activity),则外界还需要原封不动的startActivity到系统中
        // 可防止出现“本来要打开宿主,结果被改成插件”,进而无法打开宿主Activity的问题

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

        // 帮助填写打开前的Intent的ComponentName信息(如有。没有的情况如直接通过Action打开等)
        if (!TextUtils.isEmpty(plugin) && !TextUtils.isEmpty(activity)) {
            from.setComponent(new ComponentName(plugin, activity));
        }
        //插件activity查找
        ComponentName cn = mPluginMgr.mLocal.loadPluginActivity(intent, plugin, activity, process);
        ...

        // 将Intent指向到“坑位”。这样:
        // from:插件原Intent
        // to:坑位Intent
        intent.setComponent(cn);
        ...
        context.startActivity(intent);//里面是Android原生逻辑,启动坑activity

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

        return true;
    }

看下PluginCommImpl的loadPluginActivity方法是如何获取插件activity

    /**
     * 加载插件Activity,在startActivity之前调用
     * @param intent
     * @param plugin 插件名
     * @param target 目标Service名,如果传null,则取获取到的第一个
     * @param process 是否在指定进程中启动
     * @return
     */
    public ComponentName loadPluginActivity(Intent intent, String plugin, String activity, int process) {

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

        try {
            // 获取 ActivityInfo(可能是其它插件的 Activity,所以这里使用 pair 将 pluginName 也返回)
            ai = getActivityInfo(plugin, activity, intent);
            ...
            // 存储此 Activity 在插件 Manifest 中声明主题到 Intent
            intent.putExtra(INTENT_KEY_THEME_ID, ai.theme);
             
            // 根据 activity 的 processName,选择进程 ID 标识
            if (ai.processName != null) {
                process = PluginClientHelper.getProcessInt(ai.processName);
            }

            // 容器选择(启动目标进程)
            IPluginClient client = MP.startPluginProcess(plugin, process, info);
            if (client == null) {
                return null;
            }

            // 远程分配坑位,就是由于我们的activity没有在Manifist注册,坑位就是宿主中提前注册好的假activity匹配到获取的activity
            container = client.allocActivityContainer(plugin, process, ai.name, intent);
             
        } 
        ...

        PmBase.cleanIntentPluginParams(intent);
 
        //将获取的activity信息拼装到自己的Intent
        PluginIntent ii = new PluginIntent(intent);
        ii.setPlugin(plugin);
        ii.setActivity(ai.name);
        ii.setProcess(IPluginManager.PROCESS_AUTO);
        ii.setContainer(container);
        ii.setCounter(0);
        return new ComponentName(IPC.getPackageName(), container);
    }

看下getActivityInfo方法

    public ActivityInfo getActivityInfo(String plugin, String activity, Intent intent) {
        // 获取插件对象
        Plugin p = mPluginMgr.loadAppPlugin(plugin);
        ...
        ActivityInfo ai = null;

        // activity 不为空时,从插件声明的 Activity 集合中查找
        if (!TextUtils.isEmpty(activity)) {
            ai = p.mLoader.mComponents.getActivity(activity);
        } else {
            // activity 为空时,根据 Intent 匹配
            ai = IntentMatcherHelper.getActivityInfo(mContext, plugin, intent);
        }
        return ai;
    }

看下PmBase的loadAppPlugin方法,最终是调用Plugin的load方法

    final boolean load(int load, boolean useCache) {
        PluginInfo info = mInfo;
        boolean rc = loadLocked(load, useCache);//完成插件的安装
        // 尝试在此处调用Application.onCreate方法
        if (load == LOAD_APP && rc) {
            callApp();
        }
        // 如果info改了,通知一下常驻
        // 只针对P-n的Type转化来处理,一定要通知,这样Framework_Version也会得到更新
        if (rc && mInfo != info) {
            UpdateInfoTask task = new UpdateInfoTask((PluginInfo) mInfo.clone());
            Tasks.post2Thread(task);
        }
        return rc;
    }

看下loadLocked方法核心方法是里面的doLoad方法,里面的核心逻辑是Loader类的loadDex方法

    final boolean loadDex(ClassLoader parent, int load) {
        try {
            PackageManager pm = mContext.getPackageManager();

            mPackageInfo = Plugin.queryCachedPackageInfo(mPath);
            if (mPackageInfo == null) {
                // 创建PackageInfo
                mPackageInfo = pm.getPackageArchiveInfo(mPath,
                        PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES | PackageManager.GET_PROVIDERS | PackageManager.GET_RECEIVERS | PackageManager.GET_META_DATA);
                ...
                mPackageInfo.applicationInfo.sourceDir = mPath;
                mPackageInfo.applicationInfo.publicSourceDir = mPath;

                if (TextUtils.isEmpty(mPackageInfo.applicationInfo.processName)) {
                    mPackageInfo.applicationInfo.processName = mPackageInfo.applicationInfo.packageName;
                }

                // 添加针对SO库的加载
                // 此属性最终用于ApplicationLoaders.getClassLoader,在创建PathClassLoader时成为其参数
                // 这样findLibrary可不用覆写,即可直接实现SO的加载
 
                PluginInfo pi = mPluginObj.mInfo;
                File ld = pi.getNativeLibsDir();
                mPackageInfo.applicationInfo.nativeLibraryDir = ld.getAbsolutePath();
            ...

            // 创建或获取ComponentList表
            // 将插件中所有清单文件中注册的组件初始化到mComponents
            mComponents = Plugin.queryCachedComponentList(mPath);
            if (mComponents == null) {
                // ComponentList
                mComponents = new ComponentList(mPackageInfo, mPath, mPluginObj.mInfo);

                // 动态注册插件中声明的 receiver
                regReceivers();
                ... 
            }

            if (load == Plugin.LOAD_INFO) {
                return isPackageInfoLoaded();
            }
            //初始化插件中Resources
            mPkgResources = Plugin.queryCachedResources(mPath);
            // LOAD_RESOURCES和LOAD_ALL都会获取资源,但LOAD_INFO不可以(只允许获取PackageInfo)
            if (mPkgResources == null) {
                // Resources
                try {
                    if (BuildConfig.DEBUG) {
                        // 如果是Debug模式的话,防止与Instant Run冲突,资源重新New一个
                        Resources r = pm.getResourcesForApplication(mPackageInfo.applicationInfo);
                        mPkgResources = new Resources(r.getAssets(), r.getDisplayMetrics(), r.getConfiguration());
                    } else {
                        mPkgResources = pm.getResourcesForApplication(mPackageInfo.applicationInfo);
                    }
                } ...
            }
            //查找ClassLoader
            mClassLoader = Plugin.queryCachedClassLoader(mPath);
            if (mClassLoader == null) {
                // 创建ClassLoader
                ...
                mClassLoader = RePlugin.getConfig().getCallbacks().createPluginClassLoader(mPluginObj.mInfo, mPath, out, soDir, parent);
                
            }
            // 获取对应插件Context
            mPkgContext = new PluginContext(mContext, android.R.style.Theme, mClassLoader, mPkgResources, mPluginName, this);

        } ...
        return true;
    }

一个插件在被首次加载的时候一层层调用,最终会这个Loader类的loadDex方法创建插件对应的上下文Context,这个PluginContext里有mClassLoader, mPkgResources, mPluginName,有ClassLoader就可以加载class,有PkgResources就可以加载插件中资源,到这里插件就被安装初始化完毕了,完毕后,这个插件就可以被正常使用了。
回到getActivityInfo,获取到信息后就能得到manifest中注册的组件,将组件初始化到ComponentList,loadPluginActivity就可以获取插件activity。
因为activity是插件中的没有注册,直接启动会报错,所以要分配坑位,坑位就是宿主中提前注册好的假activity匹配到获取的activity。接下来看下是如何分配坑位的
allocActivityContainer方法实际是IPluginClient这个aidl文件定义的接口,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);
        ...

        if (ai.processName == null) {
            ai.processName = ai.applicationInfo.processName;
        }
        if (ai.processName == null) {
            ai.processName = ai.packageName;
        }

        /* 获取 Container */
        String container;

        // 自定义进程
        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 是否存在 */
        Class<?> c = null;
        try {
            c = p.mLoader.mClassLoader.loadClass(activity);
        } ...

        return container;
    }

看下alloc2方法,里面最终调allocLocked方法完成分配

    private final ActivityState allocLocked(ActivityInfo ai, HashMap<String, ActivityState> map,
                                            String plugin, String activity, Intent intent) {
        // 坑和状态的 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;
        }
        ...
    }

找到坑位后,回到startActivity方法里的 context.startActivity(intent)方法,里面是Android原生逻辑,启动坑activity。看下系统activity启动流程,Activity的startActivity会调用到startActivityForResult

    public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
            @Nullable Bundle options) {
        if (mParent == null) {
            options = transferSpringboardActivityOptions(options);
            Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivity(
                    this, mMainThread.getApplicationThread(), mToken, this,
                    intent, requestCode, options);
           ....
        }
    }

里面是通过Instrumentation.execStartActivity启动activity

    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
        ....
        try {
            intent.migrateExtraStreamToClipData();
            intent.prepareToLeaveProcess(who);
            int result = ActivityManager.getService()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);
            checkStartActivityResult(result, intent);
        } ...
    }

核心启动方法是ActivityManager.getService().startActivity,这个是真正完成activity启动的方法,这个时候就已经到了系统进程中,activity的启动是跨进程的binder通信。Service主要是检查activity是否注册过,我们启动的坑位activity是注册过的,会正常启动。然后回到ActivityThread,里面的performLaunchActivity方法真正完成activity加载和创建

    /**  Core implementation of activity launch. */
    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ActivityInfo aInfo = r.activityInfo;
        ...
        ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = appContext.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
         .....
    }

mInstrumentation.newActivity方法里的instantiateActivity方法完成创建

    public @NonNull Activity instantiateActivity(@NonNull ClassLoader cl, @NonNull String className,
            @Nullable Intent intent)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        return (Activity) cl.loadClass(className).newInstance();
    }

前面的过程都是通过坑位activity骗过系统的检查,然后通过这个classLoader完成坑位的替换,创建的时候创建成我们的activity,核心逻辑回到了自定义的classLoader中,接下来看下PluginDexClassLoader,看他的loadClass方法

    @Override
    protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
        // 插件自己的Class。从自己开始一直到BootClassLoader,采用正常的双亲委派模型流程,读到了就直接返回
        Class<?> pc = null;
        ClassNotFoundException cnfException = null;
        try {
            pc = super.loadClass(className, resolve);
            if (pc != null) {
                return pc;
            }
        } catch (ClassNotFoundException e) {
            // Do not throw "e" now
            cnfException = e;

            if (PluginDexClassLoaderPatch.need2LoadFromHost(className)) {
                try {
                    return loadClassFromHost(className, resolve);
                } ...
            }
        }

        // 若插件里没有此类,则会从宿主ClassLoader中找,找到了则直接返回
        // 注意:需要读取isUseHostClassIfNotFound开关。默认为关闭的。可参见该开关的说明
        if (RePlugin.getConfig().isUseHostClassIfNotFound()) {
            try {
                return loadClassFromHost(className, resolve);
            }...
    }
2 启动插件service流程源码分析

看下PluginContext类的startService方法

    @Override
    public ComponentName startService(Intent service) {
        if (mContextInjector != null) {
            mContextInjector.startServiceBefore(service);
        }

        if (mLoader.mPluginObj.mInfo.getFrameworkVersion() <= 2) {
            // 仅框架版本为3及以上的才支持
            return super.startService(service);
        }
        try {
            return PluginServiceClient.startService(this, service, true);
        } catch (PluginClientHelper.ShouldCallSystem e) {
            // 若打开插件出错,则直接走系统逻辑
            return super.startService(service);
        } finally {
            if (mContextInjector != null) {
                mContextInjector.startServiceAfter(service);
            }
        }
    }

核心方法是PluginServiceClient.startService

    public static ComponentName startService(Context context, Intent intent, boolean throwOnFail) {

        // 从 Intent 中获取 ComponentName
        ComponentName cn = getServiceComponentFromIntent(context, intent);

        // 获取将要使用服务的进程ID,并在稍后获取PSS对象
        int process = getProcessByComponentName(cn);
        if (process == PROCESS_UNKNOWN) {
            // 有可能是不支持的插件,也有可能本意就是想调用Main工程的服务。则直接调用系统方法来做
            if (throwOnFail) {
                throw new PluginClientHelper.ShouldCallSystem();
            } else {
                return context.startService(intent);
            }
        }

        // 既然确认是插件,则将之前获取的插件信息填入
        intent.setComponent(cn);
        //AIDL的调用
        IPluginServiceServer pss = sServerFetcher.fetchByProcess(process);
        ...

        // 开启服务
        try {
            return pss.startService(intent, sClientMessenger);
        } ...
    }

getProcessByComponentName中的核心方法是PluginClientHelper.getProcessInt

    public static Integer getProcessInt(String processName) {
        if (!TextUtils.isEmpty(processName)) {
            // 插件若想将组件定义成在"常驻进程"中运行,则可以在android:process中定义:

            // 1. (推荐)":GuardService"。这样无论宿主的常驻进程名是什么,都会定向到"常驻进程"
            String pntl = processName.toLowerCase();
            String ppdntl = HostConfigHelper.PERSISTENT_NAME.toLowerCase();
            if (pntl.contains(ppdntl)) {
                return IPluginManager.PROCESS_PERSIST;
            }

            // 2. 和宿主常驻进程名相同,这样也会定向到"常驻进程",但若移植到其它宿主上则会出现问题
            String ppntl = IPC.getPersistentProcessName().toLowerCase();
            if (TextUtils.equals(pntl, ppntl)) {
                return IPluginManager.PROCESS_PERSIST;
            }

            // 3. 用户自定义进程时,从 Map 中取数据
            // (根据冒号之后的名称来判断是否是自定义进程)
            processName = PluginProcessHost.processTail(processName.toLowerCase());
            if (PROCESS_INT_MAP.containsKey(processName)) {
                return PROCESS_INT_MAP.get(processName);
            }
        }
        return IPluginManager.PROCESS_UI;
    }

看下PluginServiceServer中AIDL的实现class Stub extends IPluginServiceServer.Stub里面的startServiceLocked方法实现了启动Service

    // 启动插件Service。说明见PluginServiceClient的定义
    ComponentName startServiceLocked(Intent intent, Messenger client) {
        intent = cloneIntentLocked(intent);
        ComponentName cn = intent.getComponent();
 
        final ServiceRecord sr = retrieveServiceLocked(intent);
        if (sr == null) {
            return null;
        }
        if (!installServiceIfNeededLocked(sr)) {
            return null;
        }

        sr.startRequested = true;

        // 加入到列表中,统一管理
        mServicesByName.put(cn, sr);
 
        // 从binder线程post到ui线程,去执行Service的onStartCommand操作
        Message message = mHandler.obtainMessage(WHAT_ON_START_COMMAND);
        Bundle data = new Bundle();
        data.putParcelable("intent", intent);
        message.setData(data);
        message.obj = sr;
        mHandler.sendMessage(message);

        return cn;
    }

installServiceIfNeededLocked方法里核心是installServiceLocked方法,看下是如何创建service的

    // 加载插件,获取Service对象,并将其缓存起来
    private boolean installServiceLocked(ServiceRecord sr) {
        // 通过ServiceInfo创建Service对象
        Context plgc = Factory.queryPluginContext(sr.plugin);
        ...
        ClassLoader cl = plgc.getClassLoader();
        ...

        // 构建Service对象
        Service s;
        try {
            s = (Service) cl.loadClass(sr.serviceInfo.name).newInstance();
        } ...

        // 只复写Context,别的都不做
        try {
            attachBaseContextLocked(s, plgc);//通过反射调用Service.attachBaseContext方法(Protected的)换成插件的context
        }...
        s.onCreate();//调用onCreate生命周期
        sr.service = s;

        // 开启“坑位”服务,防止进程被杀
        ComponentName pitCN = getPitComponentName();
        sr.pitComponentName = pitCN;
        startPitService(pitCN);
        return true;
    }
3 自定义进程源码分析

看下PluginServiceServerFetcher的fetchByProcess方法

    public IPluginServiceServer fetchByProcess(int process) {
         
        // 取之前的缓存
        IPluginServiceServer pss;
        synchronized (PSS_LOCKER) {
            pss = mServiceManagerByProcessMap.get(process);
            if (pss != null) {                 
                return pss;
            }
        }

        // 缓存没有?则去目标进程获取新的
        try {
            if (process == IPluginManager.PROCESS_PERSIST) {//是否是常驻进程
                IPluginHost ph = PluginProcessMain.getPluginHost();
                pss = ph.fetchServiceServer();
            } else {
                PluginBinderInfo pbi = new PluginBinderInfo(PluginBinderInfo.NONE_REQUEST);
                IPluginClient pc = MP.startPluginProcess(null, process, pbi);
                pss = pc.fetchServiceServer();
            }

            // 挂死亡周期,如果出问题了就置空重来,防止外界调用psm出现DeadObject问题
            pss.asBinder().linkToDeath(new PSSDeathMonitor(process, pss.asBinder()), 0);
        } ...
        if (pss != null) {
            synchronized (PSS_LOCKER) {
                mServiceManagerByProcessMap.put(process, pss);
            }
        }
        return pss;
    }

MP.startPluginProcess方法看进程分配和启动,里面调用了startPluginProcess,startPluginProcess方法的实现最终会走到PmBase的startPluginProcessLocked方法

    final IPluginClient startPluginProcessLocked(String plugin, int process, PluginBinderInfo info) {
        
        ...

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

        // 分配
        int index = IPluginManager.PROCESS_AUTO;
        try {
            index = PluginProcessMain.allocProcess(plugin, process);
            ...
        } ...
        // 分配的坑位不属于UI、自定义进程或Stub坑位进程,就返回。(没找到有效进程)
        if (!(index == IPluginManager.PROCESS_UI
                || PluginProcessHost.isCustomPluginProcess(index)
                || PluginManager.isPluginProcess(index))) {
            return null;
        }

        // 启动
        boolean rc = PluginProviderStub.proxyStartPluginProcess(mContext, index);
        ...

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

看下分配逻辑的allocProcess方法最终调用StubProcessManager.allocProcess方法

    static final int allocProcess(String plugin) {
        if (LOG) {
            LogDebug.d(PLUGIN_TAG, "alloc plugin process: plugin=" + plugin);
        }
        // 取运行列表
        List<ActivityManager.RunningAppProcessInfo> processes = AMSUtils.getRunningAppProcessesNoThrows(RePluginInternal.getAppContext());
        // 取运行列表失败,则直接返回失败
        if (processes == null || processes.isEmpty()) {
            ...
            return IPluginManager.PROCESS_AUTO;
        }
        //根据优先级分配坑位进程
        int prevMatchPriority = -1; //临时变量,保存上一个ProcessRecord的进程分配优先级
        ProcessRecord selectRecord = null; //被选中的坑位进程
        for (ProcessRecord r : STUB_PROCESSES) {
            synchronized (r) {
                if (r.calculateMatchPriority(plugin) > prevMatchPriority) {
                    prevMatchPriority = r.calculateMatchPriority(plugin);
                    selectRecord = r;
                } else if (r.calculateMatchPriority(plugin) == prevMatchPriority) {
                    if (r.mobified < selectRecord.mobified) {
                        selectRecord = r;
                    }
                }
            }
        }
        if (selectRecord == null) { //不应该出现
            return IPluginManager.PROCESS_AUTO;
        }
        synchronized (selectRecord){
            //插件已在分配进程中运行,直接返回
            if (selectRecord.calculateMatchPriority(plugin) == Integer.MAX_VALUE && (selectRecord.state == StubProcessState.STATE_ALLOCATED || selectRecord.state == StubProcessState.STATE_RUNNING))
            {
                return selectRecord.index;
            }
            selectRecord.resetAllocate(plugin, processes);
            return selectRecord.index;
        }
    }

看下启动逻辑的proxyStartPluginProcess方法,将信息写入ProcessPitProviderBase这个ContentProvider中,借助ContentProvider实现多进程

    static final boolean proxyStartPluginProcess(Context context, int index) {
        //
        ContentValues values = new ContentValues();
        values.put(KEY_METHOD, METHOD_START_PROCESS);
        values.put(KEY_COOKIE, PMF.sPluginMgr.mLocalCookie);
        Uri uri = context.getContentResolver().insert(ProcessPitProviderBase.buildUri(index), values);
        ...
        return true;
    }
4 广播注册接收源码分析

看Loader类的loadDex方法里的regReceivers,这个方法最终通过mPluginHost.regReceiver完成广播接收器注册,他是在PmHostSvc类的regReceiver方法实现

    public void regReceiver(String plugin, Map rcvFilMap) throws RemoteException {
        PluginInfo pi = MP.getPlugin(plugin, false);
        ...

        HashMap<String, List<IntentFilter>> receiverFilterMap = (HashMap<String, List<IntentFilter>>) rcvFilMap;

        // 遍历此插件中所有静态声明的 Receiver
        for (HashMap.Entry<String, List<IntentFilter>> entry : receiverFilterMap.entrySet()) {
            if (mReceiverProxy == null) {
                mReceiverProxy = new PluginReceiverProxy();//创建的是代理
                mReceiverProxy.setActionPluginMap(mActionPluginComponents);
            }

            /* 保存 action-plugin-receiver 的关系 */
            String receiver = entry.getKey();
            List<IntentFilter> filters = entry.getValue();

            if (filters != null) {
                for (IntentFilter filter : filters) {
                    int actionCount = filter.countActions();
                    while (actionCount >= 1) {
                        saveAction(filter.getAction(actionCount - 1), plugin, receiver);
                        actionCount--;
                    }

                    // 注册 Receiver
                    mContext.registerReceiver(mReceiverProxy, filter);
                }
            }
        }
    }

看下PluginReceiverProxy

    public void onReceive(Context context, Intent intent) {
       ...
        // 根据 action 取得 map<plugin, List<receiver>>
        HashMap<String, List<String>> pc = mActionPluginComponents.get(action);
        if (pc != null) {

            // 遍历每一个插件
            for (HashMap.Entry<String, List<String>> entry : pc.entrySet()) {
                String plugin = entry.getKey();
                if (entry.getValue() == null) {
                    continue;
                }

                // 拷贝数据,防止多线程问题
                List<String> receivers = new ArrayList<>(entry.getValue());
                // 此插件所有声明的 receiver
                for (String receiver : receivers) {
                    try {
                        // 在对应进程接收广播, 如果进程未启动,则拉起之
                        int process = getProcessOfReceiver(plugin, receiver);

                        if (process == IPluginManager.PROCESS_PERSIST) {
                            IPluginHost host = PluginProcessMain.getPluginHost();
                            host.onReceive(plugin, receiver, intent);
                        } else {
                            IPluginClient client = MP.startPluginProcess(plugin, process, new PluginBinderInfo(PluginBinderInfo.NONE_REQUEST));
                            client.onReceive(plugin, receiver, intent);
                        }

                    } ...
    }

看下client.onReceive的实现,最终调用的PluginReceiverHelper.onPluginReceiverReceived

    public static void onPluginReceiverReceived(final String plugin,
                                                final String receiverName,
                                                final HashMap<String, BroadcastReceiver> receivers,
                                                final Intent intent) {
        ...
        // 使用插件的 Context 对象
        final Context pContext = Factory.queryPluginContext(plugin);
        if (pContext == null) {
            return;
        }

        String key = String.format("%s-%s", plugin, receiverName);

        BroadcastReceiver receiver = null;
        if (receivers == null || !receivers.containsKey(key)) {
            try {
                // 使用插件的 ClassLoader 加载 BroadcastReceiver
                Class c = loadClassSafety(pContext.getClassLoader(), receiverName);
                if (c != null) {
                    receiver = (BroadcastReceiver) c.newInstance();
                    if (receivers != null) {
                        receivers.put(key, receiver);
                    }
                ...
        } else {
            receiver = receivers.get(key);
        }

        if (receiver != null) {
            final BroadcastReceiver finalReceiver = receiver;
            // 转到 ui 线程
            Tasks.post2UI(new Runnable() {
                @Override
                public void run() {
                    finalReceiver.onReceive(pContext, intent);
                }
            });
        }
    }

proxy先接收到系统的广播,然后再由PluginReceiverProxy转发到对应插件中的receiver调用它的onReceive
广播注册接收

5 启动插件ContentProvider源码分析

看PluginProviderClient类,比如其中的insert方法

    /**
     * 调用插件里的Provider
     * @see android.content.ContentResolver#insert(Uri, ContentValues)
     */
    public static Uri insert(Context c, Uri uri, ContentValues values) {
        Uri turi = toCalledUri(c, uri);
        return c.getContentResolver().insert(turi, values);
    }

toCalledUri方法

    /**
     * 将从插件里的URI转化成系统传过来的URI。可自由指定在哪个进程启动。例如:
     *
     * @param context 当前的Context对象,目前暂无用
     * @param plugin  要使用的插件
     * @param uri     URI对象
     * @param process 进程信息,若为PROCESS_AUTO,则根据插件Manifest来指定进程
     * @return 转换后可直接在ContentResolver使用的URI
     */
    public static Uri toCalledUri(Context context, String plugin, Uri uri, int process) {
        ...

        if (uri.getAuthority().startsWith(PluginPitProviderBase.AUTHORITY_PREFIX)) {
            // 自己已填好了要使用的插件名(以PluginUIProvider及其它为开头),这里不做处理
            return uri;
        }

        // content://com.qihoo360.mobilesafe.PluginUIP
        if (process == IPluginManager.PROCESS_AUTO) {
            // 直接从插件的Manifest中获取
            process = getProcessByAuthority(plugin, uri.getAuthority());
            if (process == PROCESS_UNKNOWN) {
                // 可能不是插件里的,而是主程序的,直接返回Uri即可
                return uri;
            }
        }

        String au;//分配权限
        if (process == IPluginManager.PROCESS_PERSIST) {
            au = PluginPitProviderPersist.AUTHORITY;
        } else if (PluginProcessHost.isCustomPluginProcess(process)) {
            au = PluginProcessHost.PROCESS_AUTHORITY_MAP.get(process);
        } else {
            au = PluginPitProviderUI.AUTHORITY;
        }
 
        String newUri = String.format("content://%s/%s/%s", au, plugin, uri.toString().replace("content://", ""));
        return Uri.parse(newUri);
    }

去PluginPitProviderBase类中看下uri的转化,还看insert方法,重点是看PluginProviderHelper.getProvider方法

    public ContentProvider getProvider(PluginUri pu) {
        ...
        String auth = pu.transferredUri.getAuthority();

        // 已有缓存?直接返回!
        ContentProvider cp = mProviderAuthorityMap.get(auth);
        if (cp != null) {
            return cp;
        }

        // 开始构建插件里的ContentProvider对象
        cp = installProvider(pu, auth);
        ...

        // 加入列表。下次直接读缓存
        mProviderAuthorityMap.put(auth, cp);
        ...
        return cp
    }

看下installProvider方法

    private ContentProvider installProvider(PluginUri pu, String auth) {
        // 开始尝试获取插件的ProviderInfo
        ComponentList col = Factory.queryPluginComponentList(pu.plugin);
        ...
        ProviderInfo pi = col.getProviderByAuthority(auth);
        ...

        // 通过ProviderInfo创建ContentProvider对象
        Context plgc = Factory.queryPluginContext(pu.plugin);
        
        ClassLoader cl = plgc.getClassLoader();
        
        ContentProvider cp;
        try {
            cp = (ContentProvider) cl.loadClass(pi.name).newInstance();//依然是通过ClassLoader创建ContentProvider实例
        } ...

        // 调用attachInfo方法(内部会调用onCreate)
        try {
            cp.attachInfo(plgc, pi);
        } catch (Throwable e) {
            // 有两种可能:
            // 1、第三方ROM修改了ContentProvider.attachInfo的实现
            // 2、开发者自己覆写了attachInfo方法,其中有Bug
            // 故暂时先Try-Catch,这样若插件的Provider没有使用Context对象,则也不会出现问题
            return null;
        }
        return cp;
    }

ContentProvider源码分析

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值