1. 程式人生 > >DroidPlugin原始碼分析外掛程序管理以及預註冊Activity,Service,ContentProvide的選擇

DroidPlugin原始碼分析外掛程序管理以及預註冊Activity,Service,ContentProvide的選擇

在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對外掛的管理分析完了。