DroidPlugin源码分析插件进程管理以及预注册Activity,Service,ContentProvide的选择

来源:互联网 发布:美术字在线生成器软件 编辑:程序博客网 时间:2024/05/02 04:16

在360对DroidPlugin的特点介绍中有云:
插件的四大组件完全不需要在Host程序中注册,支持Service、Activity、BroadcastReceiver、ContentProvider四大组件。
实现了进程管理,插件的空进程会被及时回收,占用内存低。

之所以支持Service,Activity,ContentProvider三大组件,是因为DroidPlugin在AndroidManifest文件中预先注册了8个运行插件的进程,每个进程预注册Service一个, ContentProvide一个,启动模式为Standard的Activity一个,启动模式为SingleTask,SingleTop和SingleInstance的Activity分别四个,启动模式为Standard的Dialog类型Activity一个,启动模式为SingleTask,SingleTop和SingleInstance的Dialog类型Activity分别四个,另外还有不运行在指定进程Service一个, ContentProvide一个,启动模式为Standard的Activity一个,启动模式为SingleTask,SingleTop和SingleInstance的Activity分别四个,启动模式为Standard的Dialog类型Activity一个,启动模式为SingleTask,SingleTop和SingleInstance的Dialog类型Activity分别四个。当需要启动一个Activity时,先用预先定义的来启动骗过系统,实际创建实例化和回调生命周期时,换回真的要启动的Activity。
那么问题来了:

问题1: 预注册了这么多的组件和进程,当在一个插件APP启动两个Activity并且都没有指定需要启动新的进程时,是不是需要找两个注册在同一个进程中的Activity来骗过系统的检查。同理当插件APP已经启动了Activity,又要启动一个Service时,如果启动的Service需要在新的进程中运行,那么我们需要选择一个没有被使用过的预注册的进程来启动它,如果Service指定在新的进程中运行,那么我们需要找到运行插件APP的进程,并使用此进程预注册的Service来启动它。

问题2 : 如果宿主进程有许多的插件Apk,他们的个数操作了预定义的进程数,当用户启动了插件Apk1,退出了,又启动了插件Apk2,又退出了,此时可能有两个空闲的进程,那么这两个空闲的进程就需要及时清理掉。以保证进程被占用满了,导致别的插件启动不起来。

那么此文就分析DroidPlugin如何处理这两个问题的。在源码分析插件运行环境初始化的一文中在IPluginManagerImpl的构造函数中,创建一个MyActivityManagerService实例。他就是这篇文章的主角。
先说说MyActivityManagerService两个重要的成员变量。
mStaticProcessList: StaticProcessList类实例,保存了所有预注册的进程,以及在每一个进程上预注册的Activity,Service,ContentProvider。当插件Apk需要运行Activity先找替代的Activity或者进程时就找它了。
mRunningProcessList:RunningProcesList类实例,保存已经因为启动插件Apk而使用的进程,以及在这个进程中运行的Activity,Service,contentProvider等等。只有这样保存起来,才能方便管理,清除那些空闲的进程。接下来开始分析MyActivityManagerService。

第一步:MyActivityManagerService初始化:

    private StaticProcessList mStaticProcessList = new StaticProcessList();    private RunningProcesList mRunningProcessList = new RunningProcesList();    public MyActivityManagerService(Context hostContext) {        super(hostContext);        mRunningProcessList.setContext(mHostContext);}

首先在加载MyActivityManagerService时,会创建两个全局变量mStaticProcessList,mRunningProcessList。
在构造函数中调用mRunningProcessList.setContext(mHostContext);
这个也比较简单只是把Context保存到mRunningProcessList中。

B:MyActivityManagerService初始化之onCreate函数:
MyActivityManagerService的onCreate函数是在IPluginManagerImpl调用所有插件函数loadAllPlugin中调用的。也就是说插件运行环境初始化完成之后,立刻调用MyActivityManagerService的onCreate函数。

    public void onCreate(IPluginManagerImpl pluginManagerImpl) throws Exception {        super.onCreate(pluginManagerImpl);        AttributeCache.init(mHostContext);        mStaticProcessList.onCreate(mHostContext);        mRunningProcessList.setContext(mHostContext);}

1 调用AttributeCache.init(mHostContext)
这个函数主要用于属性缓存主要用于缓存Window属性,判断window是否是一下三种类型,windowIsTranslucent,windowIsFloating,windowShowWallpaper。如果是的话Activity的选择会使用与定义的Dialog类型。以后在选择Activity的时候会用到。

2 mStaticProcessList.onCreate(mHostContext);
mStaticProcessList是StaticProcessList类对象相关成员变量:
mOtherProcessNames: List 主要保存宿主进程列表(除去为插件预定义的进程的其他进程)
items: Map对象,主要是以ProcessName为key,以ProcessItem为Value
processName就是AndroidManifest文件中的Android:process属性
类 ProcessItem: name String 对象,保存进程名字
activityInfos 保存name进程中预定义的Activity信息
serviceInfos 保存name进程中预定义的service信息
providerInfos 保存name进程中预定义的contentProvider信息
有了对上面成员变量的了解,再来看StaticProcessList.oncreate函数就比较容易理解了。

    void onCreate(Context mHostContext) throws NameNotFoundException {        Intent intent = new Intent(Intent.ACTION_MAIN);        intent.addCategory(CATEGORY_ACTIVITY_PROXY_STUB);        intent.setPackage(mHostContext.getPackageName());        PackageManager pm = mHostContext.getPackageManager();        List<ResolveInfo> activities = pm.queryIntentActivities(intent, PackageManager.GET_META_DATA);        for (ResolveInfo activity : activities) {            addActivityInfo(activity.activityInfo);        }        List<ResolveInfo> services = pm.queryIntentServices(intent, 0);        for (ResolveInfo service : services) {            addServiceInfo(service.serviceInfo);        }        PackageInfo packageInfo = pm.getPackageInfo(mHostContext.getPackageName(), PackageManager.GET_PROVIDERS);        if (packageInfo.providers != null && packageInfo.providers.length > 0) {            for (ProviderInfo providerInfo : packageInfo.providers) {                if (providerInfo.name != null && providerInfo.name.startsWith(ContentProviderStub.class.getName())) {                    addProviderInfo(providerInfo);                }            }        }        mOtherProcessNames.clear();        PackageInfo packageInfo1 = pm.getPackageInfo(mHostContext.getPackageName(), PackageManager.GET_ACTIVITIES                | PackageManager.GET_RECEIVERS                | PackageManager.GET_PROVIDERS                | PackageManager.GET_SERVICES);        if (packageInfo1.activities != null) {            for (ActivityInfo info : packageInfo1.activities) {                if (!mOtherProcessNames.contains(info.processName) && !items.containsKey(info.processName)) {                    mOtherProcessNames.add(info.processName);                }            }        }        if (packageInfo1.receivers != null) {            for (ActivityInfo info : packageInfo1.receivers) {                if (!mOtherProcessNames.contains(info.processName) && !items.containsKey(info.processName)) {                    mOtherProcessNames.add(info.processName);                }            }        }        if (packageInfo1.providers != null) {            for (ProviderInfo info : packageInfo1.providers) {                if (!mOtherProcessNames.contains(info.processName) && !items.containsKey(info.processName)) {                    mOtherProcessNames.add(info.processName);                }            }        }        if (packageInfo1.services != null) {            for (ServiceInfo info : packageInfo1.services) {                if (!mOtherProcessNames.contains(info.processName) && !items.containsKey(info.processName)) {                    mOtherProcessNames.add(info.processName);                }            }        }    }

一部分:搜集所有为插件预定义的进程以及对应进程中预定义的Activity,Service, ContentProvider保存在items中。
通过PackageManagerService查找Category为CATEGORY_ACTIVITY_PROXY_STUB为插件预定义的所有Activity,Service, ContentProvider,然后分别调用addActivityInfo,addServiceInfo,addProviderInfo保存到Items中processName对应的ProcessItem的activityInfos,serviceInfos, providerInfos。
addActivityInfo,addServiceInfo,addProviderInfo三个函数的逻辑基本一致以addActivityInfo为例:

    private void addActivityInfo(ActivityInfo info) {        if (TextUtils.isEmpty(info.processName)) {            info.processName = info.packageName;        }        ProcessItem item = items.get(info.processName);        if (item == null) {            item = new ProcessItem();            item.name = info.processName;            items.put(info.processName, item);        }        item.addActivityInfo(info);    }

首先查看info.processfName是否为空,即判断Activity,service,contentprovider,他们的android:process标签是否为空,如果不为空则以Info.processName为key从items中取出ProcessItem,如果ProcessItem为空则创建一个ProcessItem实例,然后添加到Items中,最后调用ProcessItem的addActivityInfo函数讲Activity添加到ProcessItem的ActivityInfos中。

二部分:获取所有除去为插件预定义的进程,宿主应用所需要的进程。
通过PackageManagerService获取PackageInfo,包含宿主进程的Activity, Service, ContentProvider, Receiver的信息,然后遍历这些信息,他们是否运行在新的进程中,如果是,是否为插件预定义的,如果不是为插件预定义的,则把processname保存在mOtherProcessNames中。

C mRunningProcessList.setContext(mHostContext);
初始化的最后一步比较简单,只是把mHostContext保存到RunningProcessList中。

第二步:MyActivityManagerService之Activity,Service, ContentProvider选择。
在分析这个之前,先看看与之密切相关的类RunningProcesList的成员变量。
mHostContext :Context 保存宿主进程的Context
items :Map以进程id(pid)为key,ProcessItem为value这里的ProcessItem和StaticProcessList的ProcessItem是两个不同的内部类。
看看RunningProcesList的内部类ProcessItem的成员变量:
stubProcessName:保存运行的代理插件进程的进程名(这个进程是预定义给插件用的真正运行的进程)。
targetProcessName:保存被代理的插件中定义的进程名(这个进程名不是真正运行的进程)。
插件中定义的指定的进程,是不能用的,因为插件是没有真正安装到系统的,所以只能让插件启动再一个预定义的进程中,这里两个参数主要是为了关联预定义的代理插件进程代理了哪个插件进程。方便后面同一个被代理的插件进程中Activity或者service或者provider的选择。
pid :运行的进程id
uid:运行进程的用户id
pkgs: 预定义的进程中运行了哪些包名的插件应用。之前不是保存过应用的签名吗,想用签名的应用是可以运行在同一个进程中的。
targetActivityInfos,targetProviderInfos,targetServiceInfos : 保存在进程中运行的所有插件Activity,provider,service。
activityInfosMap,providerInfosMap,serviceInfosMap,Map 保存预定义代理的activity,provider,service,代理了插件中哪些activity,provider,service,在运行。
有了这几个参数就能轻松管理预定义代理进程,运行了哪些插件activity,service,provider。
其实Activity,Service, ContentProvider选择的逻辑基本一致,这里以activity为例来分析:

Activity的选择函数selectStubActivityInfo

   public ActivityInfo selectStubActivityInfo(int callingPid, int callingUid, ActivityInfo targetInfo) {        runProcessGC();//下面再分析       A://判断是否是dialogStyle       B://先从正在运行的进程中查找看是否有符合条件的进程,如果有则直接使用之       C://遍历预定义进程,mStaticProcessList       1  预定义进程正在运行且为空(没有运行任何插件包)       2  预定义进程正在运行,且不为空则检查要运行的进程和已经运行的进程签名是否相同。       3  预定义进程没有运行。    }

上面大概说了函数的基本功能,接下来就以A, B, C 三段来分析这个函数:
A 判断窗口DialogStyle。

    boolean Window_windowIsTranslucent = false;        boolean Window_windowIsFloating = false;        boolean Window_windowShowWallpaper = false;        try {            Class<?> R_Styleable_Class = Class.forName("com.android.internal.R$styleable");            int[] R_Styleable_Window = (int[]) FieldUtils.readStaticField(R_Styleable_Class, "Window");            int R_Styleable_Window_windowIsTranslucent = (int) FieldUtils.readStaticField(R_Styleable_Class, "Window_windowIsTranslucent");            int R_Styleable_Window_windowIsFloating = (int) FieldUtils.readStaticField(R_Styleable_Class, "Window_windowIsFloating");            int R_Styleable_Window_windowShowWallpaper = (int) FieldUtils.readStaticField(R_Styleable_Class, "Window_windowShowWallpaper");            AttributeCache.Entry ent = AttributeCache.instance().get(targetInfo.packageName, targetInfo.theme,                    R_Styleable_Window);            if (ent != null && ent.array != null) {                Window_windowIsTranslucent = ent.array.getBoolean(R_Styleable_Window_windowIsTranslucent, false);                Window_windowIsFloating = ent.array.getBoolean(R_Styleable_Window_windowIsFloating, false);                Window_windowShowWallpaper = ent.array.getBoolean(R_Styleable_Window_windowShowWallpaper, false);            }        } catch (Throwable e) {            Log.e(TAG, "error on read com.android.internal.R$styleable", e);        }        boolean useDialogStyle = Window_windowIsTranslucent || Window_windowIsFloating || Window_windowShowWallpaper;

这段代码比较简单,主要是通过包名和theme以及R_Styleable_Window读出window相关属性并缓存在AttributeCache中。
然后判断当前窗口是否是 windowIsTranslucent,windowIsFloating ,windowShowWallpaper 。如果是其中一种则设置useDialogStyle为真。

B 查找运行的进程中是否符合条件:

    String stubProcessName1 = mRunningProcessList.getStubProcessByTarget(targetInfo);        if (stubProcessName1 != null) {            List<ActivityInfo> stubInfos = mStaticProcessList.getActivityInfoForProcessName(stubProcessName1, useDialogStyle);            for (ActivityInfo stubInfo : stubInfos) {                if (stubInfo.launchMode == targetInfo.launchMode) {                    if (stubInfo.launchMode == ActivityInfo.LAUNCH_MULTIPLE) {                        mRunningProcessList.setTargetProcessName(stubInfo, targetInfo);                        return stubInfo;                    } else if (!mRunningProcessList.isStubInfoUsed(stubInfo, targetInfo, stubProcessName1)) {                        mRunningProcessList.setTargetProcessName(stubInfo, targetInfo);                        return stubInfo;                    }                }            }        }

1 调用getStubProcessByTarget找到符合条件的代理进程。
什么是符合条件的进程呢?这里就不贴出这个函数的源码了,大概步骤如下:
遍历正在运行的代理进程items,查看进程(ProcessItem)中运行的pkgs中是否包含targetInfo的包名,并且processItem.targetProcessName(及该代理进程代理的插件进程)是否和targetInfo.processName(activity指定要运行的进程)是否相同。
如果相同则表示targetInfo所在的插件进程已经运行,则直接返回该代理进程的名字processItem.stubProcessName。
如果第一个条件不满足,则遍历进程(ProcessItem)中运行的pkgs中是否有签名和targetInfo的包的签名相同的包名。
如果有则判断processItem.targetProcessName(及该代理进程代理的插件进程)是否和targetInfo.processName(activity指定要运行的进程)是否相同。
如果相同则返回该代理进程的名字processItem.stubProcessName。
如果以上条件都不满足则返回空,返回空说明targetInfo.processName还没有启动过。

2 假设返回不为空,targetInfo.processName所描述的进程已经运行在代理进程中。
首先调用mStaticProcessList.getActivityInfoForProcessName获取在这个代理进程中预定义的所有代理activityInfo,当useDialogStyle时,返回的是预定义的dialog型的代理ActivityInfo。
然后遍历ActivityInfo,判断代理ActivityInfo的启动模式是否和目标Activity(targetInfo)的启动模式相同。
如果相同且要启动的目标Activity的启动模式是Standard,则无需做其他判断,
调用setTargetProcessName函数将目标Activity的包名添加到ProcessItem的pkgs中,并设置processItem的targetProcessName为targetInfo.processName。
然后返回该代理ActivityInfo。此时表示,已经找到合适的代理ActivityInfo来代理启动目标Activity。
如果不是Standard模式,则调用isStubInfoUsed函数判断该代理ActivityInfo是否已经代理了该目标Activity。isStubInfoUsed函数比较简单,不贴源码了,大概就是通过前面找到的代理进程名字,找到代理进程ProcessItem,并从activityInfosMap中找到代理这个代理Activity所代理的所有目标Activity集合,然后判断集合中是否存在要启动的目标Activity。
如果没有代理要启动目标Activity,调用setTargetProcessName函数然后返回此代理Activity,
如果已经代理了,则继续从当前进程中查找合适的代理Activity,找到后调用调用setTargetProcessName函数然后返回。
这个返回的代理ActivityInfo就是调用系统接口去启动的Activity,主要用于骗过系统的检查。

C: 从预定义代理进程中查找。
1 遍历预定义代理进程,找到预定义进程正在运行,并且为空(及没有运行任何插件包)的情况:

        List<String> stubProcessNames = mStaticProcessList.getProcessNames();        for (String stubProcessName : stubProcessNames) {            List<ActivityInfo> stubInfos = mStaticProcessList.getActivityInfoForProcessName(stubProcessName, useDialogStyle);            if (mRunningProcessList.isProcessRunning(stubProcessName)) {//该预定义的进程正在运行。                if (mRunningProcessList.isPkgEmpty(stubProcessName)) {//空进程,没有运行任何插件包。                    for (ActivityInfo stubInfo : stubInfos) {                         //选择合适的代理Activity和前面的一样为了缩短篇幅不贴出代码。                        }                    }                    throw throwException("没有找到合适的StubInfo");                }

首先通过mStaticProcessList.getProcessNames获取所有预定义的进程名字。开始遍历。
通过mRunningProcessList.isProcessRunning函数判断这个预定义的进程是否正在运行,isProcessRunning函数就是从items中查找是否存在processItem的stubProcessName和当前要的stubProcessName相同的ProcessItem。
如果有,则调用mRunningProcessList.isPkgEmpty判断但前进程是否为空。isPkgEmpty比较简单,补贴源码,前面说过一个成员变量pkgs,表示运行在代理插件进程的插件包。如果pkgs大小为零,这说明该代理进程为空,没有运行任何插件。
如果确实为空,就从但前进程中选择一个合适的代理Activity,代理目标Activity,这个过程这个过程A段分析中已经详细分析了。

2 预定义进程正在运行,且不为空则检查要运行的进程和已经运行的进程签名是否相同

 else if (mRunningProcessList.isPkgCanRunInProcess(targetInfo.packageName, stubProcessName, targetInfo.processName)) {                    for (ActivityInfo stubInfo : stubInfos) {                        //选择合适的代理Activity和前面的一样为了缩短篇幅不贴出代码。                    }               } else {                    //这里需要考虑签名一样的情况,多个插件公用一个进程。                }            }

首先调用mRunningProcessList.isPkgCanRunInProcess判断目标Activity是否可以启动在当前代理进程中。
isPkgCanRunInProcess函数和A段分析的函数getStubProcessByTarget非常相似,可自行查看。
当目标Activity可以运行在当前进程中时,就从当前进程中选择一个合适的代理Activity,代理目标Activity,这个过程这个过程A段分析中已经详细分析了。

3 预定义进程没有运行

    for (ActivityInfo stubInfo : stubInfos) {        //选择合适的代理Activity和前面的一样为了缩短篇幅不贴出代码。     }

当代码走到这一段说明,当前代理进程还没有运行,而且要启动的目标Activity所在的进程,也没有被代理启动(及已经运行的代理插件进程没有合适的进程来运行目标Activity)。那不是正好,一个代理进程没有运行,一个需要一个代理进程来运行,那就从当前进程中选择一个合适的代理Activity,代理目标Activity,这个过程这个过程A段分析中已经详细分析了。
另外需要指出,前面选择合适代理Activity的过程中了解到,当找到合适代理Activity返回之前调用mRunningProcessList.setTargetProcessName(stubInfo, targetInfo);保存目标Activity的包名到运行他的进程,并设置代理进程所代理的目标进程名。在3这种情况下是不起作用的。因为,mRunningProcessList.setTargetProcessName遍历的items都是正在运行的代理插件进程,而当前这个代理插件进程并没有运行启动。所以根本遍历不到。
最后,代理Activity选择的过程中,我们多次遍历了items这个已经运行的代理进程集合,而且这个集合帮助我们管理这些已经运行的插件代理进程起到了重要的作用,这个items是何时创建的呢?

第三步:MyActivityManagerService之代理插件进程的管理
代理插件进程的管理一部分是:管理好已经运行的代理插件进程以及在这个进程中运行了哪些插件包,和哪些组件。
另外一部分是:如何及时删除那些正在运行的空的代理插件进程。

A 添加管理正在运行的代理插件进程item的过程
onActivityCreated函是在目标Activity onCreate中回调的,(这里涉及插件Activity启动流程,下面的文章会详细分析)在这个函数中回调用。
mRunningProcessList.addActivityInfo(callingPid, callingUid, stubInfo, targetInfo);
addActivityInfo函数如下:

    void addActivityInfo(int pid, int uid, ActivityInfo stubInfo, ActivityInfo targetInfo) {        ProcessItem item = items.get(pid);        if (TextUtils.isEmpty(targetInfo.processName)) {            targetInfo.processName = targetInfo.packageName;        }        if (item == null) {            item = new ProcessItem();            item.pid = pid;            item.uid = uid;            items.put(pid, item);        }        item.stubProcessName = stubInfo.processName;        if (!item.pkgs.contains(targetInfo.packageName)) {            item.pkgs.add(targetInfo.packageName);        }        item.targetProcessName = targetInfo.processName;        item.addActivityInfo(stubInfo.name, targetInfo);    }

前面已经分析过items, item.pid, item.uid, item.pkgs, item.subProcessName, item.targetProcessName, 他们的作用,这个函数就比较简单了。
当从items中找不到对应pid的processItem时,创建一个processItem设置pid,uid,然后添加到items中,在设置好,stubProcessName, 以及所代理的目标进程,targetProcessName, 并把代理的目标Activity的包名添加到pkgs中,
最后调用processItem 的addActivityInfo函数把代理的目标activity添加到另外两个变量targetActivityInfos和activityInfosMap中。

B 清除代理插件进程中运行的组件信息以Activity为列:
onActivityDestory是在目标Activity onDestory中回调的。
onActivityDestory如下:

   public void onActivityDestory(int callingPid, int callingUid, ActivityInfo stubInfo, ActivityInfo targetInfo) {        mRunningProcessList.removeActivityInfo(callingPid, callingUid, stubInfo, targetInfo);        runProcessGC();    }

先调用mRunningProcessList.removeActivityInfo函数清除要销毁的目标Activity。
removeActivityInfo函数如下:

       ProcessItem item = items.get(pid);        if (TextUtils.isEmpty(targetInfo.processName)) {            targetInfo.processName = targetInfo.packageName;        }        if (item != null) {            item.removeActivityInfo(stubInfo.name, targetInfo);        }

首先从items中找到要销毁的目标activity所运行的插件代理进程,然后调用processItem的removeActivityInfo函数:
removeActivityInfo函数就不贴源码了,他主要完成了,从targetActivityInfos和activityInfosMap中移除目标Activity信息,然后更新代理插件进程中的pkgs。
当目标Activity是插件包中还在最后一个组件,那么目标Activity所对应的pkg就会从pkgs中移除。

C: 插件代理进程管理的最后一个部分,及时清理空的或者运行优先级比较低的插件代理进程,以保重有足够的插件代理进程来加载更多的插件。
runProcessGC在个函数在selectStubActivityInfo函数中有看到,而且在选择service,provider,以及它们的销毁函数中也能看到。这个函数的作用就是及时清除运行优先级低的或者空的插件代理进程:
runProcessGC函数如下:

    private void runProcessGC() {        List<RunningAppProcessInfo> infos = am.getRunningAppProcesses();        List<RunningAppProcessInfo> myInfos = new ArrayList<RunningAppProcessInfo>();        if (infos == null || infos.size() < 0) {            return;        }        List<String> pns = mStaticProcessList.getOtherProcessNames();        pns.add(mHostContext.getPackageName());        for (RunningAppProcessInfo info : infos) {            if (info.uid == android.os.Process.myUid()                    && info.pid != android.os.Process.myPid()                    && !pns.contains(info.processName)                    && mRunningProcessList.isPlugin(info.pid)                    && !mRunningProcessList.isPersistentApplication(info.pid)                    /*&& !mRunningProcessList.isPersistentApplication(info.pid)*/) {                myInfos.add(info);            }        }        Collections.sort(myInfos, sProcessComparator);        for (RunningAppProcessInfo myInfo : myInfos) {            if (myInfo.importance == RunningAppProcessInfo.IMPORTANCE_GONE) {                doGc(myInfo);            } else if (myInfo.importance == RunningAppProcessInfo.IMPORTANCE_EMPTY) {                doGc(myInfo);            } else if (myInfo.importance == RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {                doGc(myInfo);            } else if (myInfo.importance == RunningAppProcessInfo.IMPORTANCE_SERVICE) {                doGc(myInfo);            } /*else if (myInfo.importance == RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE) {                //杀死进程,不能保存状态。但是关我什么事?            }*/ else if (myInfo.importance == RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE) {                //杀死进程            } else if (myInfo.importance == RunningAppProcessInfo.IMPORTANCE_VISIBLE) {                //看得见            } else if (myInfo.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {                //前台进程。            }        }    }

分析这段代码前先了解两个问题:
1 应用持久化 android:persistent 和com.morgoo.droidplugin.EXTRA_APP_PERSISTENT
在应用在AndroidManifest文件中中设置了android:persistent ,说明该应用需要从开机就开始运行,中间不会退出,即使内存不足,也不会将这样的进程杀死。适合系统应用开发比如说Phone系统的Phone应用。
同样的原理,DroidPlugin设置了一个这样的标签:com.morgoo.droidplugin.EXTRA_APP_PERSISTENT当插件应用设置了这样一个标签后,代理该插件运行的代理插件进程启动后,只要该插件应用还有一个组件运行在该代理插件进程中,就不会被当前这套清除后台优先级低的代码逻辑清除。

2 进程优先级:Android系统试图尽可能长时间地保持应用程序进程,但为了新建或者运行更加重要的进程,总是需要清除过时进程来回收内存。为了决定保留或终止哪个进程,根据进程内运行的组件及这些组件的状态,系统把每个进程都划入一个“重要性层次结构”中。重要性最低的进程首先会被清除,然后是下一个最低的,依此类推,这都是恢复系统资源所必需的。 Android官方文档描述如下:
int IMPORTANCE_BACKGROUND
Constant for importance: this process process contains background code that is expendable.

int IMPORTANCE_EMPTY
Constant for importance: this process is empty of any actively running code.

int IMPORTANCE_FOREGROUND
Constant for importance: this process is running the foreground UI.

int IMPORTANCE_PERCEPTIBLE
Constant for importance: this process is running something that is considered to be actively perceptible to the user.

int IMPORTANCE_SERVICE
Constant for importance: this process is contains services that should remain running.

int IMPORTANCE_VISIBLE
Constant for importance: this process is running something that is actively visible to the user, though not in the immediate foreground.

int REASON_PROVIDER_IN_USE
Constant for importanceReasonCode: one of the application’s content providers is being used by another process.

int REASON_SERVICE_IN_USE
Constant for importanceReasonCode: one of the application’s content providers is being used by another process.

int REASON_UNKNOWN
Constant for importanceReasonCode: nothing special has been specified for the reason for this level.

有了这些了解,分析这个函数就比较简单了:
1 通过ActivityManagerService获取当前所有正在运行的进程列表infos。
调用mStaticProcessList.getOtherProcessNames获取宿主进程需要用到的进程,并把宿主进程名(就是包名添加进去)pns。
创建变量myInfos用于保存插件进程。

2 遍历当前运行的进程列表infos,判断是否是可能需要删除的代理插件进程。
info.uid == android.os.Process.myUid()
判断条件一:当前进程和宿主进程同一个uid
info.pid != android.os.Process.myPid()
判断条件二:当前进程pid和宿主进程 pid不一致
!pns.contains(info.processName)
判断条件三:当前进程不是宿主进程需要用到的进程(及不包含在pns中)
mRunningProcessList.isPlugin(info.pid)
判断条件四:当前进程是在正在运行的代理插件进程中(及通过pid能在items中找 到对应的processItems)
!mRunningProcessList.isPersistentApplication(info.pid)
判断条件五:不是持久化应用或者插件(前面已经有说明,具体代码比较简单可自行查看)
当满足这五个条件就把当前进程保存到myInfos。

3 遍历myInfos代理插件进程列表,当进程优先级为:
RunningAppProcessInfo.IMPORTANCE_GONE
RunningAppProcessInfo.IMPORTANCE_EMPTY
RunningAppProcessInfo.IMPORTANCE_BACKGROUND
RunningAppProcessInfo.IMPORTANCE_SERVICE
时调用doGc
doGc函数如下:

    private void doGc(RunningAppProcessInfo myInfo) {        int activityCount = mRunningProcessList.getActivityCountByPid(myInfo.pid);        int serviceCount = mRunningProcessList.getServiceCountByPid(myInfo.pid);        int providerCount = mRunningProcessList.getProviderCountByPid(myInfo.pid);        if (activityCount <= 0 && serviceCount <= 0 && providerCount <= 0) {            //杀死空进程。               android.os.Process.killProcess(myInfo.pid);        } else if (activityCount <= 0 && serviceCount > 0) {            List<String> names = mRunningProcessList.getStubServiceByPid(myInfo.pid);            if (names != null && names.size() > 0) {                for (String name : names) {                    Intent service = new Intent();                    service.setClassName(mHostContext.getPackageName(), name);                    AbstractServiceStub.startKillService(mHostContext, service);               }            }        }    }

这个函数首先从RunningProcessList获取运行在该代理插件进程中的Activity个数,service个数,provider个数,
判断如果当前进程运行的三个组件个数都为零,则直接杀掉该进程。
判断如果activity组件为零,service组件大于零,则获取所有service名,调用AbstractServiceStub.startKillService,这个函数所完成的工作如下(源码会在服务篇分析):查看AbstractServiceStub的Service中是否运行其他Service,如果没有运行其他Service,这停止AbstractServiceStub,并关闭其所运行的进程。
至此DroidPlugin对插件的管理分析完了。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 小米笔记本忘记开机密码怎么办 小米手机儿童模式忘记密码怎么办 小米应用锁密码忘了怎么办 小米air密码忘了怎么办 小米4密码忘了怎么办 小米手机开机密码忘了怎么办? 小米笔记本电脑开机密码忘了怎么办 小米笔记本开机密码忘了怎么办 htc手机忘记解锁图案怎么办 红米手机屏幕锁定怎么解锁怎么办 小米5s有id怎么办 手机密码找不回来了怎么办? 手机密码图案忘了怎么办 手机屏幕图案锁忘了怎么办 捡到苹果7有id锁怎么办 魅族什么都忘了怎么办 海信电视百事通登陆失败怎么办 去哪儿换号了怎么办 ipan充不进去电怎么办 安卓数据线松了怎么办 索尼z5耳机掉漆怎么办 索尼z5无限重启怎么办 苹果8基带坏了怎么办 oppo手机忘记图案密码怎么办 电池充不进去电怎么办 电脑充不进去电怎么办 苹果5c白苹果怎么办 港行不支持电信卡怎么办 安卓导航不开机怎么办 鞭炮放一半不响怎么办 禁止鸣笛的地方鸣笛了怎么办 手被炮仗炸了怎么办 手被猴子抓伤了怎么办 炸东西剩的油怎么办 炸臭豆腐剩的油怎么办 油炸久了油发黑怎么办 炸鱼的时候粘锅怎么办 吃了葱蒜有味怎么办 哺乳期喝了抹茶怎么办 干炸小黄鱼凉了怎么办 烧鱼酱油放多了怎么办