Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android ActivityManagerService(AMS)的進程管理

Android ActivityManagerService(AMS)的進程管理

編輯:關於Android編程

Android中的AMS的職責是比較多的,其中一個比較重要的職責就是app進程的管理,比如我們調用startActivity方法啟動一個activity的時候,可能對應的那個進程沒有啟動,因此需要啟動那個進程,而且對於這個進程還要有一些必要的管理過程,比如將它放到LRU(least recently used)列表中去等。本文就AMS的進程管理基本邏輯和過程做一個簡要的分析,以幫助大家弄清楚AMS的進程管理。

Android的應用進程是什麼?

這裡我們講的是android的應用進程,並不是native層的進程,這裡需要知道。在android中,進程(Process)的概念是被弱化的,我們知道在傳統的系統中進程是靜態程序執行的載體,程序的代碼段,數據等信息全部都是在進程的管理之中的,而且進程中的多個組件都是在一個進程中的,並且他們的生命周期都是和進程息息相關的。但是在android中,進程只不過是一系列運行組件的容器而已,這些組件可以運行在不同的進程之中,也就是說某個app中的某個組件可以運行在本地進程之中,也可以運行在另外一個進程中;說白了,也就是說android的app中的組件只是一個靜態的程序級別的概念,真正運行的時候這些組件可能“各立山頭”,互補相關;要做到這一點也很容易,只要在AndroidManifest中的相應組件指定android:process屬性就可以了。同時,不同的app中的不同組件也可以運行在一個進程中,可以通過在AndroidManifest指定相應的進程名稱就可以了。
雖然在android的開發中,不再強調進程的概念,但是進程畢竟是實際存在於android系統中,只是它和我們認識到的傳統進程不太一樣,所以我們的AMS還是需要對進程進行管理的。AMS對於進程的管理主要體現在兩個方面:第一是動態調整進程再mLruProcess中的位置,第二就是調整進程的oom_adj的值,這兩項都和系統的內存自動回收有關系,當系統的內存不足時,系統主要根據oom_adj的值來選擇殺死一些進程以釋放內存,這個值越大表示進程越容易被殺死。

AMS啟動進程流程

AMS是調用addAppLocked方法來啟動一個進程的,這個方法實現如下:

    final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated,
            String abiOverride) {
        ProcessRecord app;
        // isolated表示當前需要啟動的app是不是一個獨立的進程,如果是獨立的話那就重新
        // 創建一個ProcessRecord,如果不是的話那就從正在運行的進程列表中找。
        if (!isolated) {
            app = getProcessRecordLocked(info.processName, info.uid, true);
        } else {
            app = null;
        }

        // 這是一個獨立的進程或者在正在運行的列表中沒有找到相應的記錄
        if (app == null) {
            // 新建一個ProcessRecord對象
            app = newProcessRecordLocked(info, null, isolated, 0);
            // 更新lru列表和oom_adj值,下面我們會重點分析這裡
            updateLruProcessLocked(app, false, null);
            updateOomAdjLocked();
        }

        // This package really, really can not be stopped.
        // 這裡將當前的app包設置為啟動狀態,這樣這個app就可以接受系統的隱式intent了
        try {
            AppGlobals.getPackageManager().setPackageStoppedState(
                    info.packageName, false, UserHandle.getUserId(app.uid));
        } catch (RemoteException e) {
        } catch (IllegalArgumentException e) {
            Slog.w(TAG, "Failed trying to unstop package "
                    + info.packageName + ": " + e);
        }

        // 如果app中帶有persistent標記的話,那個對新建的app對象做相應的標記
        if ((info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
            app.persistent = true;
            // 這個值下面我們分析oom_adj中會說明
            app.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
        }

        // 如果app中的thread(就是主線程)為空的話
        if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
            mPersistentStartingProcesses.add(app);
            // 實際啟動app進程
            startProcessLocked(app, "added application", app.processName, abiOverride,
                    null /* entryPoint */, null /* entryPointArgs */);
        }

        return app;
    }

addAppLocked方法會根據參數isolated來決定這個進程是不是一個獨立的進程,如果是那就創建一個新的ProcessRecord對象,如果不是的話,那就調用getProcessRecordLocked方法在當前運行的進程列表中查找進程,我們看下這個方法的定義:
[email protected]

    final ProcessRecord getProcessRecordLocked(String processName, int uid, boolean keepIfLarge) {
        // 如果是一個系統級別的UID,也就是說這個app是由於system用戶啟動的進程
        if (uid == Process.SYSTEM_UID) {
            // The system gets to run in any process.  If there are multiple
            // processes with the same uid, just pick the first (this
            // should never happen).
            // 上面英文注釋說的很明白,如果有多個system uid的進程的話,那就取第一個
            SparseArray procs = mProcessNames.getMap().get(processName);
            if (procs == null) return null;
            final int procCount = procs.size();
            for (int i = 0; i < procCount; i++) {
                final int procUid = procs.keyAt(i);
                // 如果這是一個合理的app進程,或者uid不是要求的uid的話,那就跳過。
                // 原因下面的英文注釋說的很清楚。
                if (UserHandle.isApp(procUid) || !UserHandle.isSameUser(procUid, uid)) {
                    // Don't use an app process or different user process for system component.
                    continue;
                }
                return procs.valueAt(i);
            }
        }

        // 如果不是系統uid的話,就會執行到這裡。首先從mProcessNames查找正在運行的進程記錄
        ProcessRecord proc = mProcessNames.get(processName, uid);
        // 上面這個分支永遠不會執行,這是一個用戶測試的分支。
        if (false && proc != null && !keepIfLarge
                && proc.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY
                && proc.lastCachedPss >= 4000) {
            // Turn this condition on to cause killing to happen regularly, for testing.
            if (proc.baseProcessTracker != null) {
                proc.baseProcessTracker.reportCachedKill(proc.pkgList, proc.lastCachedPss);
            }
            proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", true);
        // 一般會走這個分支,但是由於我們的addAppLocked傳遞進來的keepIfLarge是true,因此這個分支也不會走。
        } else if (proc != null && !keepIfLarge
                && mLastMemoryLevel > ProcessStats.ADJ_MEM_FACTOR_NORMAL
                && proc.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
            if (DEBUG_PSS) Slog.d(TAG_PSS, "May not keep " + proc + ": pss=" + proc.lastCachedPss);
            if (proc.lastCachedPss >= mProcessList.getCachedRestoreThresholdKb()) {
                if (proc.baseProcessTracker != null) {
                    proc.baseProcessTracker.reportCachedKill(proc.pkgList, proc.lastCachedPss);
                }
                proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", true);
            }
        }

        // 直接就將找到的記錄對象返回。
        return proc;
    }

getProcessRecordLocked方法的邏輯是比較簡單的,他分為兩部分:uid是system uid和uid是普通uid兩種情況。具體的邏輯上面的注釋已經解釋,這裡就不贅述。需要補充的是,上面如果我們的uid是system uid的話那麼會使用UserHandle.isApp來判斷這是不是一個app進程,他的實現如下:
[email protected]

    /** @hide */
    public static boolean isApp(int uid) {
        if (uid > 0) {
            final int appId = getAppId(uid);
            return appId >= Process.FIRST_APPLICATION_UID && appId <= Process.LAST_APPLICATION_UID;
        } else {
            return false;
        }
    }

這裡的邏輯很簡單,主要就是根據uid獲得app的id,然後如果app id是在Process.FIRST_APPLICATION_UID和Process.LAST_APPLICATION_UID之間(這是正常app id應該處於的范圍)的話,那就是一個合理的app進程。
現在我們回到addAppLocked方法,我們剛才分析了如果不是獨立進程的情況的邏輯,總結來說就是通過getProcessRecordLocked查找當前系統中正在運行的進程記錄,並且把這個記錄保存下來。現在我們看一下如果請求的是一個獨立的進程的話(這也是最常見的情形),處理的方式是什麼樣的:
[email protected]

    final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated,
            String abiOverride) {
        ProcessRecord app;
        if (!isolated) {
            app = getProcessRecordLocked(info.processName, info.uid, true);
        } else {
            app = null;
        }

        if (app == null) {
            app = newProcessRecordLocked(info, null, isolated, 0);
            updateLruProcessLocked(app, false, null);
            updateOomAdjLocked();
        }

我們看到,如果是一個獨立的進程的話,那麼首先app肯定就直接賦值為null,接下來我們會調用newProcessRecordLocked新建一個ProcessRecord對象,這個方法的具體處理因為和我們AMS的進程管理不是特別相關,我們就不分析了,感興趣的讀者可以自行分析。新建了一個ProcessRecord對象之後的操作就是最重要的操作了:

updateLruProcessLocked(app, false, null);
updateOomAdjLocked();

這兩步操作設計AMS管理進程的核心工作,我們稍後詳細分析,我們先接下來的邏輯,接下來的邏輯中最重要的就是調用startProcessLocked方法實際啟動一個進程:

            startProcessLocked(app, "added application", app.processName, abiOverride,
                    null /* entryPoint */, null /* entryPointArgs */);

在AMS中startProcessLocked方法實現了多態,但是根據這裡的參數我們可以確定我們調用的方法是哪一個。startProcessLocked方法比較長,這裡我們分部來分析:
[email protected]

    private final void startProcessLocked(ProcessRecord app, String hostingType,
            String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
        long startTime = SystemClock.elapsedRealtime();
        if (app.pid > 0 && app.pid != MY_PID) {
            checkTime(startTime, "startProcess: removing from pids map");
            synchronized (mPidsSelfLocked) {
                mPidsSelfLocked.remove(app.pid);
                mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
            }
            checkTime(startTime, "startProcess: done removing from pids map");
            app.setPid(0);
        }

首先需要記錄一下app的啟動時間,這個數據是給checkTime方法使用的,checkTime方法的定義如下:
[email protected]

    private void checkTime(long startTime, String where) {
        long now = SystemClock.elapsedRealtime();
        if ((now-startTime) > 1000) {
            // If we are taking more than a second, log about it.
            Slog.w(TAG, "Slow operation: " + (now-startTime) + "ms so far, now at " + where);
        }
    }

我們看到,checkTime的邏輯很簡單,主要就是看看當前的時間和傳遞進來的時間是不是相差1000ms,如果相差1s的話那就需要打下log,這部分的邏輯就是這樣。我們繼續startProcessLocked的分析:

if (app.pid > 0 && app.pid != MY_PID) {
            checkTime(startTime, "startProcess: removing from pids map");
            synchronized (mPidsSelfLocked) {
                mPidsSelfLocked.remove(app.pid);
                mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
            }
            checkTime(startTime, "startProcess: done removing from pids map");
            app.setPid(0);
        }

再獲得啟動開始時間之後,當app的pid大於0並且pid不是本進程PID的話,就要把當前pid的進程從mPidsSelfLocked列表中移除,防止重復,因為後面我們還要加入;然後就是移除PROC_START_TIMEOUT_MSG消息,這個消息是AMS用來控制app啟動時間的,如果啟動超時了就發出效果消息,下面我們會設置這個消息,現在需要取消之前設置的消息,防止干擾。
接下來的邏輯:

mProcessesOnHold.remove(app);

這裡是將app進程從mProcessesOnHold列表中清除,這個列表是什麼呢?這個列表是系統中在AMS沒有啟動之前請求啟動的app,這些app當時沒有啟動,被hold了,當AMS啟動完成的時候需要將他們啟動;現在如果我們啟動的app就在這個列表中的, 那麼自然需要將它移除,防止重復啟動。我們可以從mProcessesOnHold的定義的地方看到這一點:

    /**
     * List of records for processes that someone had tried to start before the
     * system was ready.  We don't start them at that point, but ensure they
     * are started by the time booting is complete.
     */
    final ArrayList mProcessesOnHold = new ArrayList();

接下來就要更新cpu使用統計信息:

updateCpuStats();

cpu統計使用信息也是AMS的一個重要的任務,這個任務就是通過啟動一個獨立的線程去獲得cpu的使用情況,我們看一下updateCpuStats的實現:

    void updateCpuStats() {
        final long now = SystemClock.uptimeMillis();
        if (mLastCpuTime.get() >= now - MONITOR_CPU_MIN_TIME) {
            return;
        }
        if (mProcessCpuMutexFree.compareAndSet(true, false)) {
            synchronized (mProcessCpuThread) {
                mProcessCpuThread.notify();
            }
        }
    }

我們發現它的邏輯很簡單,首先是判斷當前時間是不是和上次檢查時間相差MONITOR_CPU_MIN_TIME(5s)以上,如果是就繼續,如果不是就返回,因為不能過於平凡做這件事情,它比較耗電。工作的方式就是喚醒mProcessCpuThread去采集cpu的信息。這個線程的實現我們這裡先不分析,這塊和我們的進程管理相關不是很密切,我後面的文章會詳細分析這塊的內容。在startProcessLocked的接下來的邏輯中,主要就是app啟動的一些參數設置,條件檢查等操作,這裡我們直接略過,我們直接看實際進程啟動的部分:

// Start the process.  It will either succeed and return a result containing
            // the PID of the new process, or else throw a RuntimeException.
            boolean isActivityProcess = (entryPoint == null);
            if (entryPoint == null) entryPoint = "android.app.ActivityThread";
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
                    app.processName);
            checkTime(startTime, "startProcess: asking zygote to start proc");
            // 實際啟動了一個進程
            Process.ProcessStartResult startResult = Process.start(entryPoint,
                    app.processName, uid, uid, gids, debugFlags, mountExternal,
                    app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
                    app.info.dataDir, entryPointArgs);
            checkTime(startTime, "startProcess: returned from zygote!");
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

上面代碼中最核心的代碼就是調用Process.start靜態方法部分,這個調用會通過socket和zygote建立鏈接請求,fork新進程,我們看一下start方法實現:

    public static final ProcessStartResult start(final String processClass,
                                  final String niceName,
                                  int uid, int gid, int[] gids,
                                  int debugFlags, int mountExternal,
                                  int targetSdkVersion,
                                  String seInfo,
                                  String abi,
                                  String instructionSet,
                                  String appDataDir,
                                  String[] zygoteArgs) {
        try {
            return startViaZygote(processClass, niceName, uid, gid, gids,
                    debugFlags, mountExternal, targetSdkVersion, seInfo,
                    abi, instructionSet, appDataDir, zygoteArgs);
        } catch (ZygoteStartFailedEx ex) {
            Log.e(LOG_TAG,
                    "Starting VM process through Zygote failed");
            throw new RuntimeException(
                    "Starting VM process through Zygote failed", ex);
        }
    }

這裡的startViaZygote方法會和zygote建立鏈接並且創建進程,這裡我們就不詳細分析zygote部分的fork邏輯了。我們再次回到startProcessLocked方法中分析剩下的邏輯,剛才我們上面的請求zygote的部分是一個異步請求,會立即返回,當時實際進程啟動需要多長的時間是不確定的,但是我們不能無限制等待,需要有一個啟動超時機制,在startProcessLocked接下來的邏輯中就啟動了一個延遲消息:

synchronized (mPidsSelfLocked) {
                this.mPidsSelfLocked.put(startResult.pid, app);
                if (isActivityProcess) {
                    Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
                    msg.obj = app;
                    mHandler.sendMessageDelayed(msg, startResult.usingWrapper
                            ? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
                }
            }

這個消息是PROC_START_TIMEOUT_MSG用於通知應用啟動超時了,由mHandler負責處理。並且超時的閥值是根據當前啟動的進程是不是一個wrapper進程來決定的,如果是wrapper進程的話那超時就是PROC_START_TIMEOUT_WITH_WRAPPER(1200s),如果不是則PROC_START_TIMEOUT(10s),正常情況下都是後者,wrapper進程使用場景較少。為了弄清楚假如發生了啟動超時,系統怎麼處理,我們還需要看一下mHandler對這個消息的處理:
[email protected]

case PROC_START_TIMEOUT_MSG: {
                if (mDidDexOpt) {
                    mDidDexOpt = false;
                    Message nmsg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
                    nmsg.obj = msg.obj;
                    mHandler.sendMessageDelayed(nmsg, PROC_START_TIMEOUT);
                    return;
                }
                ProcessRecord app = (ProcessRecord)msg.obj;
                synchronized (ActivityManagerService.this) {
                    processStartTimedOutLocked(app);
                }
            } break;

首先需要判斷當前這個app是不處在dex優化過程中,如果是處在dex優化過程中的話,那麼app的啟動時間很長可以理解,因為實際的啟動需要等到dex優化完畢才能進行,這個時候就直接再等PROC_START_TIMEOUT(10s)之後再檢查。我們看下mDidDexOpt的定義就知道了:

    /**
     * This is set if we had to do a delayed dexopt of an app before launching
     * it, to increase the ANR timeouts in that case.
     */
    boolean mDidDexOpt;

這裡的注釋寫的很清楚了。在mHandler中如果不是在dex優化中的話,那麼就要調用processStartTimedOutLocked方法來處理啟動超時的數據和資源回收的操作了,並且需要停止當前這個任務,同時以ANR的方式通知用戶,這部分的邏輯這裡就不詳述了。
到現在位置我們弄明白了android是怎麼啟動一個app進程的了,主要就是通過addAppLocked方法實現,這個方法會調用startProcessLocked方法實現,而這個方法很長,主要就是一些app進程啟動數據和參數檢查和設置,最後就是調用Process類的靜態方法start方法和zygote建立鏈接fork進程。了解了AMS怎麼啟動一個進程之後,我們接下來分析一下AMS怎麼管理進程的。

AMS的進程管理之LRU管理

前面我們在分析app進程的啟動流程的時候,我們看到系統調用updateLruProcessLocked和updateOomAdjLocked來調控設置進程,其實在AMS中有很多地方都會調用到他們。我們首先來看下updateLruProcessLocked方法,這個方法用來調整某個進程在mLruProcesses列表中的位置,mLruProcesses是最近使用進程列表的意思。每當進程中的activity或者service等組件發生變化的時候,就意味著相應的進程發生了活動,因此調用這個方法將該進程調整到盡可能高的位置,同時還要更新關聯進程的位置。在mLruProcesses這個列表中,最近運行的進程是從前往後排列的,也就是說越是處於前端的這個進程就越是最近使用的,同時需要注意的是擁有activity的進程的位置總是高於只有service的進程,因為activity可以和用戶發生實際的交互,而後台的service是不可以的。
還要需要說明的是,在分析LRU管理機制之前,我們需要關注以下兩個變量:

    /**
     * Where in mLruProcesses that the processes hosting activities start.
     */
    int mLruProcessActivityStart = 0;

    /**
     * Where in mLruProcesses that the processes hosting services start.
     * This is after (lower index) than mLruProcessesActivityStart.
     */
    int mLruProcessServiceStart = 0;

就如同注釋解釋的那樣,第一個變量總是指向列表中位置最高的帶有activity進程和沒有activity只有service的進程,並且mLruProcessServiceStart總是在mLruProcessActivityStart的後面。還需要注意的是mLruProcesses的定義:

    /**
     * List of running applications, sorted by recent usage.
     * The first entry in the list is the least recently used.
     */
    final ArrayList mLruProcesses = new ArrayList();

我們看到mLruProcesses只是一個ArrayList,在mLruProcesses中,某個成員的index越大,就表示這個app越是最近使用的,這一點在後面的分析中很重要。
接下來我們看一下updateLruProcessLocked的實現,這個方法比較長,我們分段來分析這個方法的實現:

    final void updateLruProcessLocked(ProcessRecord app, boolean activityChange,
            ProcessRecord client) {
        final boolean hasActivity = app.activities.size() > 0 || app.hasClientActivities
                || app.treatLikeActivity;
        final boolean hasService = false; // not impl yet. app.services.size() > 0;

首先我們看到這裡定義一個變量hasActivity用來表示某個app中是否包含activity組件,以下三種情況都可以看做是有activity組件的:app本身確實包含activity組件;app本身有service,並且有另外一個含有activity的app鏈接到此app的service上;該app啟動serivce的時候帶有標記BIND_TREAT_LIKE_ACTIVITY。接下來的變量hasService只是定義了,但是沒有實際的實現,從後面的注釋我們也能看出這一點(Google的代碼還是有待開發的,有很多功能還是沒有完善的,android系統源碼中有很多這樣的代碼),所以我們後面分析的時候和這個變量相關的代碼可以直接跳過。接下來的代碼如下:

if (!activityChange && hasActivity) {
            // The process has activities, so we are only allowing activity-based adjustments
            // to move it.  It should be kept in the front of the list with other
            // processes that have activities, and we don't want those to change their
            // order except due to activity operations.
            return;
        }

這裡的代碼中注釋寫的很清楚,這裡的意思就是,如果當前app中有activity組件,並且不是activity發生了改變(啟動或者銷毀等),那就直接返回,因為這個時候沒有必要更新LRU。
我們繼續看下面的代碼:

        // 這是系統中的一個計數器,記錄本方法被調用了多少次,也就是LRU更新了多少次。
        mLruSeq++;
        // 記錄更新時間
        final long now = SystemClock.uptimeMillis();
        app.lastActivityTime = now;

        // First a quick reject: if the app is already at the position we will
        // put it, then there is nothing to do.
        // 上面英文注釋寫的很清楚,就是查看我們要插入的app是不是已經在mLruProcesses頂端了,如果是就不用插入了。
        if (hasActivity) {
            // 有activity的情況
            final int N = mLruProcesses.size();
            if (N > 0 && mLruProcesses.get(N-1) == app) {
                if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, already top activity: " + app);
                return;
            }
        } else {
            // 沒有activity情況,可以暫時理解為有service的情況(真正有service的判定暫時沒有實現呢!!)
            if (mLruProcessServiceStart > 0
                    && mLruProcesses.get(mLruProcessServiceStart-1) == app) {
                if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, already top other: " + app);
                return;
            }
        }

上面的代碼意圖我的注釋中已經寫清楚了,基本邏輯也很簡單。下面我們繼續分析代碼:

        int lrui = mLruProcesses.lastIndexOf(app);

        if (app.persistent && lrui >= 0) {
            // We don't care about the position of persistent processes, as long as
            // they are in the list.
            if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, persistent: " + app);
            return;
        }

這部分的代碼主要就是要排除persistent app,這部分app不會被殺死,永遠在運行,因此沒有必要針對這樣的app進程進行LRU排序。

        if (lrui >= 0) {
            if (lrui < mLruProcessActivityStart) {
                mLruProcessActivityStart--;
            }
            if (lrui < mLruProcessServiceStart) {
                mLruProcessServiceStart--;
            }
            mLruProcesses.remove(lrui);
        }

這部分代碼邏輯也很簡單,首先判斷一下我們需要更新的app所在的index,如果小於mLruProcessActivityStart,那麼mLruProcessActivityStart需要減1,同理如果小於mLruProcessServiceStart,那麼mLruProcessServiceStart也需要減1,這兩個變量的含義上面已經解釋,這裡操作因為我們下面即將remove掉這個app進程記錄(我們要把它放到合適的地方去),所以這裡都要減1.
接下來的代碼就是主要操作代碼了,分為3種情況操作:有activity的,有service的(沒有實現,我們略過),其他的(暫時可以理解為有service的)。我們首先看一下有activity的情況:

        // 這個變量指向我們操作app進程記錄的下一個index
        int nextIndex;
        if (hasActivity) {
            final int N = mLruProcesses.size();
            // 這個分支表示目前app沒有activity,並且鏈接到它的service中的app有activity,這裡就把這個app添加到mLruProcesses的第二位,因為第一位必須是實際包含activity的app進程
            if (app.activities.size() == 0 && mLruProcessActivityStart < (N - 1)) {
                // Process doesn't have activities, but has clients with
                // activities...  move it up, but one below the top (the top
                // should always have a real activity).
                if (DEBUG_LRU) Slog.d(TAG_LRU,
                        "Adding to second-top of LRU activity list: " + app);
                mLruProcesses.add(N - 1, app);
                // To keep it from spamming the LRU list (by making a bunch of clients),
                // we will push down any other entries owned by the app.
                // 為了防止某個app中的service綁定了一群client從而導致LRU中頂部大部分都是這些client,這裡需要將這些client往下移動,以防止某些app通過和某個app的service綁定從而提升自己在LRU中位置。
                final int uid = app.info.uid;
                for (int i = N - 2; i > mLruProcessActivityStart; i--) {
                    ProcessRecord subProc = mLruProcesses.get(i);
                    if (subProc.info.uid == uid) {
                        // We want to push this one down the list.  If the process after
                        // it is for the same uid, however, don't do so, because we don't
                        // want them internally to be re-ordered.
                        if (mLruProcesses.get(i - 1).info.uid != uid) {
                            if (DEBUG_LRU) Slog.d(TAG_LRU,
                                    "Pushing uid " + uid + " swapping at " + i + ": "
                                    + mLruProcesses.get(i) + " : " + mLruProcesses.get(i - 1));
                            ProcessRecord tmp = mLruProcesses.get(i);
                            mLruProcesses.set(i, mLruProcesses.get(i - 1));
                            mLruProcesses.set(i - 1, tmp);
                            i--;
                        }
                    } else {
                        // A gap, we can stop here.
                        break;
                    }
                }
            // 如果進程有activity,就把它放到頂端(很多情況下都是走這個邏輯)
            } else {
                // Process has activities, put it at the very tipsy-top.
                if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU activity list: " + app);
                mLruProcesses.add(app);
            }
            nextIndex = mLruProcessServiceStart;
        }

上面的代碼展示了當某個應用的hasActivity為true的時候的邏輯,上面的注釋說的很清楚,這裡不再贅述,只是讀者需要仔細理解一下這裡的代碼,才能理解其中的含義。我們繼續看接下來的代碼:

        // 這個分支不會執行,因為hasService判定沒有實現,總是為false
        else if (hasService) {
            // Process has services, put it at the top of the service list.
            if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU service list: " + app);
            mLruProcesses.add(mLruProcessActivityStart, app);
            nextIndex = mLruProcessServiceStart;
            mLruProcessActivityStart++;
        // 一般就是走這個分支了,目前可以理解為有service的情況
        } else  {
            // Process not otherwise of interest, it goes to the top of the non-service area.
            // 我們直接定義index為mLruProcessServiceStart,因為此時的位置最高只能是mLruProcessServiceStart這個位置
            int index = mLruProcessServiceStart;
            // 一般情況下client都是null
            if (client != null) {
                // If there is a client, don't allow the process to be moved up higher
                // in the list than that client.
                int clientIndex = mLruProcesses.lastIndexOf(client);
                if (DEBUG_LRU && clientIndex < 0) Slog.d(TAG_LRU, "Unknown client " + client
                        + " when updating " + app);
                if (clientIndex <= lrui) {
                    // Don't allow the client index restriction to push it down farther in the
                    // list than it already is.
                    clientIndex = lrui;
                }
                if (clientIndex >= 0 && index > clientIndex) {
                    index = clientIndex;
                }
            }
            if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding at " + index + " of LRU list: " + app);
            // 將app進程記錄對象插入到index位置
            mLruProcesses.add(index, app);
            nextIndex = index-1;
            mLruProcessActivityStart++;
            mLruProcessServiceStart++;
        }

到這裡我們的LRU基本就更新完畢,下面的代碼對這個列表還需要進行一定的微調:

        // If the app is currently using a content provider or service,
        // bump those processes as well.
        for (int j=app.connections.size()-1; j>=0; j--) {
            ConnectionRecord cr = app.connections.valueAt(j);
            if (cr.binding != null && !cr.serviceDead && cr.binding.service != null
                    && cr.binding.service.app != null
                    && cr.binding.service.app.lruSeq != mLruSeq
                    && !cr.binding.service.app.persistent) {
                nextIndex = updateLruProcessInternalLocked(cr.binding.service.app, now, nextIndex,
                        "service connection", cr, app);
            }
        }
        for (int j=app.conProviders.size()-1; j>=0; j--) {
            ContentProviderRecord cpr = app.conProviders.get(j).provider;
            if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq && !cpr.proc.persistent) {
                nextIndex = updateLruProcessInternalLocked(cpr.proc, now, nextIndex,
                        "provider reference", cpr, app);
            }
        }

這裡的微調分為兩種情況,第一是將和本進程的service關聯的client進程的位置調整到本進程之後,第二是將和本進程ContentProvider關聯的client進程位置調整到本進程之後。調整的方式都是使用updateLruProcessInternalLocked方法,我們看一下updateLruProcessInternalLocked方法的定義:
[email protected]

    private int updateLruProcessInternalLocked(ProcessRecord app, long now, int index,
            String what, Object obj, ProcessRecord srcApp) {
        app.lastActivityTime = now;

        // 如果有activity,不做調整,因為不屬於這個調整范圍
        if (app.activities.size() > 0) {
            // Don't want to touch dependent processes that are hosting activities.
            return index;
        }

        int lrui = mLruProcesses.lastIndexOf(app);
        // 如果進程不在mLruProcesses中就返回
        if (lrui < 0) {
            Slog.wtf(TAG, "Adding dependent process " + app + " not on LRU list: "
                    + what + " " + obj + " from " + srcApp);
            return index;
        }

        // 如果目前進程的位置高於需要調整的位置,那麼不做調整,返回
        if (lrui >= index) {
            // Don't want to cause this to move dependent processes *back* in the
            // list as if they were less frequently used.
            return index;
        }

        // 如果目前進程的位置比mLruProcessActivityStart還要高,無需調整
        if (lrui >= mLruProcessActivityStart) {
            // Don't want to touch dependent processes that are hosting activities.
            return index;
        }

        // 把app調整到index-1的位置
        mLruProcesses.remove(lrui);
        if (index > 0) {
            index--;
        }
        if (DEBUG_LRU) Slog.d(TAG_LRU, "Moving dep from " + lrui + " to " + index
                + " in LRU list: " + app);
        mLruProcesses.add(index, app);
        // 返回目前進程的位置
        return index;
    }

我們前面mLruProcessActivityStart調用這個方法的時候傳遞的index參數都是nextIndex,也就是說需要把新插入的進程記錄的下一個進行微調,最高就是放到mLruProcessServiceStart的位置。
到這裡我們就基本分析完了LRU的更新過程,現在我們總結一下它的流程:updateLruProcessLocked方法中調整進程很重要的一個依據就是進程有沒有活動的activity,除了進程本身存在activity對象外,如果和進程中運行的serivice相關聯的客戶進程中有activity也算是本進程擁有activity。如果一個進程擁有activity的話,那麼把這個進程放到列表的最高位置,否則只會把它放到沒有activity的前面。調整某個進程的位置之後,還有調整和該進程的關聯進程的位置,進程的關聯進程有兩種類型:綁定本進程servrice的進程和鏈接了本進程的ContentProvider的進程。同時,如果這些進程本身是有activity的話,那麼就不調整,需要調整的是那些沒有activity的進程,在updateLruProcessInternalLocked方法中會做這種調整。

AMS的進程管理之oom_adj管理

在介紹AMS的進程oom_adj管理之前,我們先了解一下什麼是oom_adj。我們知道android運行的設備一般都是內存資源比較有限的嵌入式移動設備,在這些設備上的內存資源是十分寶貴的,因此我們需要妥善的使用和管理,總的來說就是:在合適的時候,將必要的內存資源釋放出來。傳統的linux的做法就是將不需要的進程直接殺死,為重要的進程運行騰出“空”來。但是問題來了,你根據什麼來決定是否殺死一個進程呢?或者說你怎麼知道某個進程是可以殺死的?仿照linux的做法,我們可以給每一個進程標記一個優先級,但是android中的進程和linux中的進程又不太一樣了,進程之間可以通過binder綁定,如果殺死綁定的一端,另一端也可能會出問題。那麼android是怎麼做的呢,android引入了oom_adj這個東西,所謂的oom_adj就是out of memory adjustment中文叫做內存溢出調節器,這麼翻譯有點生硬,說的簡單點這個東西就是一個用來標記某個app進程的重要性,這個值越小說明它越重要,越大就越有可能在必要的時候被“干掉”。
下面我們先看一下android中都定義了那些oom_adj的值,這些值定義在/frameworks/base/services/core/java/com/android/server/am/ProcessList.java中:

    // OOM adjustments for processes in various states:

    // Adjustment used in certain places where we don't know it yet.
    // (Generally this is something that is going to be cached, but we
    // don't know the exact value in the cached range to assign yet.)
    static final int UNKNOWN_ADJ = 16;

    // This is a process only hosting activities that are not visible,
    // so it can be killed without any disruption.
    static final int CACHED_APP_MAX_ADJ = 15;
    static final int CACHED_APP_MIN_ADJ = 9;

    // The B list of SERVICE_ADJ -- these are the old and decrepit
    // services that aren't as shiny and interesting as the ones in the A list.
    static final int SERVICE_B_ADJ = 8;

    // This is the process of the previous application that the user was in.
    // This process is kept above other things, because it is very common to
    // switch back to the previous app.  This is important both for recent
    // task switch (toggling between the two top recent apps) as well as normal
    // UI flow such as clicking on a URI in the e-mail app to view in the browser,
    // and then pressing back to return to e-mail.
    static final int PREVIOUS_APP_ADJ = 7;

    // This is a process holding the home application -- we want to try
    // avoiding killing it, even if it would normally be in the background,
    // because the user interacts with it so much.
    static final int HOME_APP_ADJ = 6;

    // This is a process holding an application service -- killing it will not
    // have much of an impact as far as the user is concerned.
    static final int SERVICE_ADJ = 5;

    // This is a process with a heavy-weight application.  It is in the
    // background, but we want to try to avoid killing it.  Value set in
    // system/rootdir/init.rc on startup.
    static final int HEAVY_WEIGHT_APP_ADJ = 4;

    // This is a process currently hosting a backup operation.  Killing it
    // is not entirely fatal but is generally a bad idea.
    static final int BACKUP_APP_ADJ = 3;

    // This is a process only hosting components that are perceptible to the
    // user, and we really want to avoid killing them, but they are not
    // immediately visible. An example is background music playback.
    static final int PERCEPTIBLE_APP_ADJ = 2;

    // This is a process only hosting activities that are visible to the
    // user, so we'd prefer they don't disappear.
    static final int VISIBLE_APP_ADJ = 1;

    // This is the process running the current foreground app.  We'd really
    // rather not kill it!
    static final int FOREGROUND_APP_ADJ = 0;

    // This is a process that the system or a persistent process has bound to,
    // and indicated it is important.
    static final int PERSISTENT_SERVICE_ADJ = -11;

    // This is a system persistent process, such as telephony.  Definitely
    // don't want to kill it, but doing so is not completely fatal.
    static final int PERSISTENT_PROC_ADJ = -12;

    // The system process runs at the default adjustment.
    static final int SYSTEM_ADJ = -16;

    // Special code for native processes that are not being managed by the system (so
    // don't have an oom adj assigned by the system).
    static final int NATIVE_ADJ = -17;

這裡google的注釋說的很清楚,我就不多廢話,但是有幾個值是比較重要的:
FOREGROUND_APP_ADJ:這表示進程有一個正在和用戶交互的界面,這個千萬不能殺死,除非萬不得已。
VISIBLE_APP_ADJ:這個表示進程中的某個UI組件是可以被用戶看見的,但是並沒有和用戶發生交互,比如一個被dialog擋住部分的activity,這個進程也是盡量不能殺死的,因為用戶是可以看見它的,殺死的話會影響用戶的體驗。
PERCEPTIBLE_APP_ADJ:正如注釋裡面說的那樣,這個adj表示進程中的某個組件可以被用戶感知到,比如說一個正在播放音樂的進程。
BACKUP_APP_ADJ:當前進程正在執行一個備份的操作,這個操作最好不要打斷,否則會出現不可修復的數據錯誤。
CACHED_APP_MIN_ADJ:這是cache進程的最小的adj的值
CACHED_APP_MAX_ADJ:cache進程的最大adj的值
這裡我們提到一個cache進程概念,那麼什麼是cache進程呢?我們在解釋這個之前,先看一下android中的進程生命周期吧。在android中不僅UI組件有生命周期,我們的進程也是有生命周期的,關於android生命周期google有說明的文檔:
https://developer.android.com/guide/components/processes-and-threads.html
這裡我只是重點解釋一下google的文檔,這個文檔中解釋了在android中的進程的5種生命周期:
1. Foreground process
原文解釋:
這裡寫圖片描述
意思是說,只要某個進程有處於前台的組件在運行,只要滿足上面的5個條件就算是前台進程。
2. Visible process
這裡寫圖片描述
可見進程的表面意思就是這個進程中的組件用戶是可以看到的,很多情況下確實如此,但是還有另外一種情況,那就是它的service綁定到了一個運行於前台的activity或這UI組件。
3. Service process
這裡寫圖片描述
服務進程就是指一個擁有後台運行service的進程,這個進程在後台為用戶和設備的交互提供運行支撐。
4. Background process
這裡寫圖片描述
後台進程就是指,一個帶有activity的進程,但是這個activity已經放到後台了,onStop方法已經被回調了;這個時候用戶已經看不見這個activity了,所以這個進程對用戶的體驗是沒有直接影響的。因此,在某些需要內存的時候,我們可以將這個進程殺死。一般而言我們的系統中有很多後台進程,比如我們我們運行了一個沒有service的app,然後我們按下home鍵回到桌面,那麼這個進程就是後台進程了。
5. Empty process
這裡寫圖片描述
空近程,顧名思義,就是進程中什麼組件都沒有了,當Background process中的後台activity被destroy的時候他就變成了空進程。空進程是所有進程中優先級最低的,是最可能被殺死的進程。
回到我們剛才說的cache進程,所謂的cache進程就是上面說的Background process。當我們的進程沒有任何後台的時候,如果它退出了前台,android系統是不會立即將它殺死的。因為,用戶很可能還會在不就的將來再次回到這個應用中,這個時候如果我們把它cache起來了,下次用戶再次啟動這個進程的時候速度就快了,這對於用戶體驗是很重要的;另外,這些cache進程確實占用了內存,如果我們需要內存的時候,就首先殺死這些cache進程。舉個形象點的例子,android中的所有的進程就像監獄中的犯人,而cache進程就是那些被判死刑,但是緩期執行的犯人。
理解了什麼是cache進程之後,我們開始分析AMS是怎麼管理oom_adj的。所謂的oom_adj的管理就是為每一個進程設置一個合理的oom_adj值。在AMS中,oom_adj值的設置是通過updateOomAdjLocked方法完成,這個方法比較長,我們分段來分析:
[email protected]

    final void updateOomAdjLocked() {
        final ActivityRecord TOP_ACT = resumedAppLocked();
        final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null;
        final long now = SystemClock.uptimeMillis();
        final long oldTime = now - ProcessList.MAX_EMPTY_TIME;
        final int N = mLruProcesses.size();

這裡是一些變量的初始化,比較重要的是這裡首先獲得了位於前台的activity所在的進程,通過resumedAppLocked方法獲得,resumedAppLocked會通過ActivityStackSupervisor.java中的resumedAppLocked來獲取,這部分涉及AMS的activity的管理,我們先不分析,後面我們分析AMS的activity的管理的時候我們再說明這一塊。這裡還要注意的是oldTime,oldTime是用當前時間減去MAX_EMPTY_TIME獲得的,MAX_EMPTY_TIME的定義如下:

    // We allow empty processes to stick around for at most 30 minutes.
    static final long MAX_EMPTY_TIME = 30*60*1000;

它的長度是30min,從上面的注釋我們也可以看到,所有的空進程的保留時間都是30min,30min後就會殺死它,後面我們會看到這個操作。
我們繼續updateOomAdjLocked的分析:

        // Reset state in all uid records.
        for (int i=mActiveUids.size()-1; i>=0; i--) {
            final UidRecord uidRec = mActiveUids.valueAt(i);
            if (false && DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
                    "Starting update of " + uidRec);
            uidRec.reset();
        }

接下來就是將mActiveUids中的每一個UidRecord對象中的curProcState都重置為PROCESS_STATE_CACHED_EMPTY:
[email protected]

    public void reset() {
        curProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
    }

UidRecord是用來表示一個uid啟動的進程記錄,而AMS中的mActiveUids是一個SparseArray,存儲了每一個uid的對應的最新運行的進程,我們看下mActiveUids定義就知道了:

    /**
     * Track all uids that have actively running processes.
     */
    final SparseArray mActiveUids = new SparseArray<>();

這裡的注釋說的很清楚了。返回到updateOomAdjLocked中,我們把mActiveUids每一個UidRecord的當前進程狀態reset為PROCESS_STATE_CACHED_EMPTY,是因為我們下面需要給他一個合適的值。
我們繼續查看updateOomAdjLocked接下來的代碼:

        mAdjSeq++;
        mNewNumServiceProcs = 0;
        mNewNumAServiceProcs = 0;

首先更新oom_adj更新計數,然後將和service process有關的兩個變量賦值為0,後面的代碼會操作這個變量,我們到時候再講解。
接下來,就是獲得系統後台進程限制的情況了:

        // 表示後台可以運行的空進程數量
        final int emptyProcessLimit;
        // 表示後台可以運行的緩存進程的數量
        final int cachedProcessLimit;
        // mProcessLimit變量表示系統中一共可以有多少個後台進程(empty + cache)
        if (mProcessLimit <= 0) {
            emptyProcessLimit = cachedProcessLimit = 0;
        } else if (mProcessLimit == 1) {
            emptyProcessLimit = 1;
            cachedProcessLimit = 0;
        } else {
            emptyProcessLimit = ProcessList.computeEmptyProcessLimit(mProcessLimit);
            cachedProcessLimit = mProcessLimit - emptyProcessLimit;
        }

上面的代碼就是獲得系統有關後台進程數目的限制設置,系統中後台進程總數目不能超過mProcessLimit個,這個變量的初始值是:

    int mProcessLimit = ProcessList.MAX_CACHED_APPS;

MAX_CACHED_APPS定義如下:

    // The maximum number of cached processes we will keep around before killing them.
    // NOTE: this constant is *only* a control to not let us go too crazy with
    // keeping around processes on devices with large amounts of RAM.  For devices that
    // are tighter on RAM, the out of memory killer is responsible for killing background
    // processes as RAM is needed, and we should *never* be relying on this limit to
    // kill them.  Also note that this limit only applies to cached background processes;
    // we have no limit on the number of service, visible, foreground, or other such
    // processes and the number of those processes does not count against the cached
    // process limit.
    static final int MAX_CACHED_APPS = 32;

我們看到了,初始值系統中的所有後台進程,也就是空進程和cache進程的總和不能超過32個。但是,用戶可以設置這個值,在settings的開發者選項中可以設置,如下圖:
這裡寫圖片描述
用戶可以點擊上圖中的“後台進程限制”來設置對應的值。
不過一般情況下,這個值都是32.現在我們就以32為值來分析一下代碼,我們看到上面的代碼中最終會走到最後一個分支,這個分支中針對emptyProcessLimit和cachedProcessLimit分別賦值,emptyProcessLimit的值是通過ProcessList的computeEmptyProcessLimit獲取的:
[email protected]

    public static int computeEmptyProcessLimit(int totalProcessLimit) {
        return totalProcessLimit/2;
    }

我們看到這裡的操作很簡單,直接把mProcessLimit除以2,然後返回。這裡除以2的意思就是空進程和cache進程數目對半分,各為16個。
我們繼續看接下來的代碼:

        // Let's determine how many processes we have running vs.
        // how many slots we have for background processes; we may want
        // to put multiple processes in a slot of there are enough of
        // them.
        int numSlots = (ProcessList.CACHED_APP_MAX_ADJ
                - ProcessList.CACHED_APP_MIN_ADJ + 1) / 2;
        int numEmptyProcs = N - mNumNonCachedProcs - mNumCachedHiddenProcs;
        if (numEmptyProcs > cachedProcessLimit) {
            // If there are more empty processes than our limit on cached
            // processes, then use the cached process limit for the factor.
            // This ensures that the really old empty processes get pushed
            // down to the bottom, so if we are running low on memory we will
            // have a better chance at keeping around more cached processes
            // instead of a gazillion empty processes.
            numEmptyProcs = cachedProcessLimit;
        }
        int emptyFactor = numEmptyProcs/numSlots;
        if (emptyFactor < 1) emptyFactor = 1;
        int cachedFactor = (mNumCachedHiddenProcs > 0 ? mNumCachedHiddenProcs : 1)/numSlots;
        if (cachedFactor < 1) cachedFactor = 1;
        int stepCached = 0;
        int stepEmpty = 0;
        int numCached = 0;
        int numEmpty = 0;
        int numTrimming = 0;

        mNumNonCachedProcs = 0;
        mNumCachedHiddenProcs = 0;

這段代碼首先計算了cache進程的slot數目,結果是(15-9+1)/2=3,然後是計算了空進程的數目,直接用mLruProcesses總數目減去mNumNonCachedProcs(非cache進程,正常進程)和mNumCachedHiddenProcs(cache進程)得到。然後就是emptyFactor(每個slot的empty的進程數)和cachedFactor(每個slot的cache進程數)的計算,方法是對應的總數目除以slot數目。這些變量有什麼意義呢?下面的代碼給了我們答案:

// First update the OOM adjustment for each of the
// application processes based on their current state.
// 獲得cache adj值
int curCachedAdj = ProcessList.CACHED_APP_MIN_ADJ;
int nextCachedAdj = curCachedAdj+1;
int curEmptyAdj = ProcessList.CACHED_APP_MIN_ADJ;
int nextEmptyAdj = curEmptyAdj+2;
// 從mLruProcesses的頂部,循環操作
for (int i=N-1; i>=0; i--) {
    ProcessRecord app = mLruProcesses.get(i);
    // 如果這個app進程沒有被AMS殺死並且主線程不為空(已經運行)的話,就執行下面操作
    if (!app.killedByAm && app.thread != null) {
        app.procStateChanged = false;
        // 通過computeOomAdjLocked計算app進程的oom_adj值,這是一個重要操作,下面我們詳細分析
        computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now);

        // If we haven't yet assigned the final cached adj
        // to the process, do that now.
        // 如上面的注釋,如果我們還沒有設置oom_adj值的話,換句話說,那就是我們經過computeOomAdjLocked計算之後我們的oom_adj的值還是比UNKNOWN_ADJ要大,說明這個進程是cache進程或者空進程
        if (app.curAdj >= ProcessList.UNKNOWN_ADJ) {
            switch (app.curProcState) {
                // 以下兩個case說明是cache進程
                case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY:
                case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
                    // This process is a cached process holding activities...
                    // assign it the next cached value for that type, and then
                    // step that cached level.
                    app.curRawAdj = curCachedAdj;
                    // 通過modifyRawOomAdj調整當前app進程的oom_adj的值
                    app.curAdj = app.modifyRawOomAdj(curCachedAdj);
                    if (DEBUG_LRU && false) Slog.d(TAG_LRU, "Assigning activity LRU #" + i
                            + " adj: " + app.curAdj + " (curCachedAdj=" + curCachedAdj
                            + ")");
                    // 沒有達到最大值CACHED_APP_MAX_ADJ
                    if (curCachedAdj != nextCachedAdj) {
                        stepCached++;
                        // 如果當前slot中的cache進程數大於前面算出的cachedFactor的話就要進入下一個slot,這麼做的目的就是使得從CACHED_APP_MIN_ADJ到CACHED_APP_MAX_ADJ之間的cache進程數目分配均勻,否則就會出現很多優先級相差很多的cache進程擁有相近的oom_adj的值,這樣的結果是不合理的。
                        if (stepCached >= cachedFactor) {
                            stepCached = 0;
                            curCachedAdj = nextCachedAdj;
                            nextCachedAdj += 2;
                            if (nextCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) {
                                nextCachedAdj = ProcessList.CACHED_APP_MAX_ADJ;
                            }
                        }
                    }
                    break;
                // 這個case就是empty進程了,處理的邏輯和上面的cache進程是一樣的
                default:
                    // For everything else, assign next empty cached process
                    // level and bump that up.  Note that this means that
                    // long-running services that have dropped down to the
                    // cached level will be treated as empty (since their process
                    // state is still as a service), which is what we want.
                    app.curRawAdj = curEmptyAdj;
                    app.curAdj = app.modifyRawOomAdj(curEmptyAdj);
                    if (DEBUG_LRU && false) Slog.d(TAG_LRU, "Assigning empty LRU #" + i
                            + " adj: " + app.curAdj + " (curEmptyAdj=" + curEmptyAdj
                            + ")");
                    if (curEmptyAdj != nextEmptyAdj) {
                        stepEmpty++;
                        if (stepEmpty >= emptyFactor) {
                            stepEmpty = 0;
                            curEmptyAdj = nextEmptyAdj;
                            nextEmptyAdj += 2;
                            if (nextEmptyAdj > ProcessList.CACHED_APP_MAX_ADJ) {
                                nextEmptyAdj = ProcessList.CACHED_APP_MAX_ADJ;
                            }
                        }
                    }
                    break;
            }
        }

        // 應用更新oom_adj後的數據,主要就是更新進程的各種oom_adj的值
        applyOomAdjLocked(app, true, now);

        // Count the number of process types.
        // 統計各種類型進程的數目
        switch (app.curProcState) {
            case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY:
            case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
                mNumCachedHiddenProcs++;
                numCached++;
                if (numCached > cachedProcessLimit) {
                    app.kill("cached #" + numCached, true);
                }
                break;
            case ActivityManager.PROCESS_STATE_CACHED_EMPTY:
                if (numEmpty > ProcessList.TRIM_EMPTY_APPS
                        && app.lastActivityTime < oldTime) {
                    app.kill("empty for "
                            + ((oldTime + ProcessList.MAX_EMPTY_TIME - app.lastActivityTime)
                            / 1000) + "s", true);
                } else {
                    numEmpty++;
                    if (numEmpty > emptyProcessLimit) {
                        app.kill("empty #" + numEmpty, true);
                    }
                }
                break;
            default:
                mNumNonCachedProcs++;
                break;
        }

        // 下面的注釋寫的很清楚,意思就是如果這個app進程是獨立的,並且這個app中沒有任何的service,那麼這個進程就沒有存在的必要的,直接殺死。
        if (app.isolated && app.services.size() <= 0) {
            // If this is an isolated process, and there are no
            // services running in it, then the process is no longer
            // needed.  We agressively kill these because we can by
            // definition not re-use the same process again, and it is
            // good to avoid having whatever code was running in them
            // left sitting around after no longer needed.
            app.kill("isolated not needed", true);
        } else {
            // Keeping this process, update its uid.
            // 否則就保留這個進程
            final UidRecord uidRec = app.uidRecord;
            if (uidRec != null && uidRec.curProcState > app.curProcState) {
                uidRec.curProcState = app.curProcState;
            }
        }

        // 如果這個app進程的重要級別比home的級別低的話,那麼就增加trim計數,這個計數在後面清除多余app進程時會用到。
        if (app.curProcState >= ActivityManager.PROCESS_STATE_HOME
                && !app.killedByAm) {
            numTrimming++;
        }
    }
}

上面代碼中我的注釋將這段程序的基本邏輯描述了,這裡就不再贅述。這裡需要額外說明一下,我們上面通過調用modifyRawOomAdj方法調整對應app進程的oom_adj值,我們下面看一下是怎麼調整的:
[email protected]

    int modifyRawOomAdj(int adj) {
        if (hasAboveClient) {
            // If this process has bound to any services with BIND_ABOVE_CLIENT,
            // then we need to drop its adjustment to be lower than the service's
            // in order to honor the request.  We want to drop it by one adjustment
            // level...  but there is special meaning applied to various levels so
            // we will skip some of them.
            if (adj < ProcessList.FOREGROUND_APP_ADJ) {
                // System process will not get dropped, ever
            } else if (adj < ProcessList.VISIBLE_APP_ADJ) {
                adj = ProcessList.VISIBLE_APP_ADJ;
            } else if (adj < ProcessList.PERCEPTIBLE_APP_ADJ) {
                adj = ProcessList.PERCEPTIBLE_APP_ADJ;
            } else if (adj < ProcessList.CACHED_APP_MIN_ADJ) {
                adj = ProcessList.CACHED_APP_MIN_ADJ;
            } else if (adj < ProcessList.CACHED_APP_MAX_ADJ) {
                adj++;
            }
        }
        return adj;
    }

這裡的操作分兩種情況:如果hasAboveClient為true和false。hasAboveClient定義如下:

boolean hasAboveClient;     // Bound using BIND_ABOVE_CLIENT, so want to be lower

這裡的注釋和上面modifyRawOomAdj中的注釋解釋的很清楚了,意思就是說如果本進程使用BIND_ABOVE_CLIENT標記綁定某個service的話,我們需要做出一定調整,調整的方式整體都是降低優先級,根據我們上面傳遞進來的參數我們知道這裡應該走最後一個分支,也就是說將adj的值加1,為什麼是這樣呢?我們看下api文檔中對BIND_ABOVE_CLIENT的解釋:
這裡寫圖片描述
google的解釋很清楚了,這裡的意思就是說client使用BIND_ABOVE_CLIENT綁定service的話,那麼client的優先級是比service要低的,雖然android系統並不會保證一定是這樣。正是因為這個,上面我們的代碼才將adj的值增加處理。現在我們再總結一下上面代碼的邏輯,主要就是從mLruProcesses中的最頂端(index最大的)開始挨個計算oom_adj的值,然後針對計算後的值進行一定的調整,就這樣。
上面我們看了oom_adj的修改機制,接下來的代碼就是將mLruProcesses列表中的進程占用的內存trim一下,主要通過調用ApplicationThread的scheduleTrimMemory來實現,這個方法最終會調用到WindowManagerService的trimMemory方法要求app進程釋放多余的內存,此處會回調app中Application對象的onTrimMemory方法通知app。這部分的代碼和我們的oom_adj相關不是很大,因此,這部分的代碼讀者可以自行分析。
上面我們分析updateOomAdjLocked的時候,我們發現updateOomAdjLocked是通過調用computeOomAdjLocked方法來計算oom_adj的,那麼computeOomAdjLocked究竟是怎麼計算的呢?下面我們來分析一下,由於這個方法非常長(其實google應該縮短這個方法,將其拆分成幾個獨立的方法,這樣代碼的可讀性更好),這裡我就針對重點代碼片段分析一下,大家主要理解其主要意圖即可:
1). 如果是當前進程的話(TOP_APP),即包含了當前顯示的activity的進程,則將他們的oom_adj設置為FOREGROUND_APP_ADJ:

if (app == TOP_APP) {
    // The last app on the list is the foreground app.
    adj = ProcessList.FOREGROUND_APP_ADJ;
    schedGroup = Process.THREAD_GROUP_DEFAULT;
    app.adjType = "top-activity";
    foregroundActivities = true;
    procState = PROCESS_STATE_TOP;
}

2). 如果進程有instrumentation實例,說明這個app正在測試當中,這個時候它的adj應該是一個前台adj,保證盡量不被殺死:

if (app.instrumentationClass != null) {
    // Don't want to kill running instrumentation.
    adj = ProcessList.FOREGROUND_APP_ADJ;
    schedGroup = Process.THREAD_GROUP_DEFAULT;
    app.adjType = "instrumentation";
    procState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
}

3). 如果進程注冊了廣播接受器,則把它的oom_adj設置為前台adj:

if ((queue = isReceivingBroadcast(app)) != null) {
    // An app that is currently receiving a broadcast also
    // counts as being in the foreground for OOM killer purposes.
    // It's placed in a sched group based on the nature of the
    // broadcast as reflected by which queue it's active in.
    adj = ProcessList.FOREGROUND_APP_ADJ;
    schedGroup = (queue == mFgBroadcastQueue)
            ? Process.THREAD_GROUP_DEFAULT : Process.THREAD_GROUP_BG_NONINTERACTIVE;
    app.adjType = "broadcast";
    procState = ActivityManager.PROCESS_STATE_RECEIVER;
}

4). 如果進程中有正在運行的service,則把它的oom_adj設置為前台adj:

if (app.executingServices.size() > 0) {
    // An app that is currently executing a service callback also
    // counts as being in the foreground.
    adj = ProcessList.FOREGROUND_APP_ADJ;
    schedGroup = app.execServicesFg ?
            Process.THREAD_GROUP_DEFAULT : Process.THREAD_GROUP_BG_NONINTERACTIVE;
    app.adjType = "exec-service";
    procState = ActivityManager.PROCESS_STATE_SERVICE;
    //Slog.i(TAG, "EXEC " + (app.execServicesFg ? "FG" : "BG") + ": " + app);
}

5). 其他的進程就先設置oom_adj為cachedAdj,在updateOomAdjLocked中我們傳遞的參數是UNKNOWN_ADJ。
上面的幾個步驟只是計算了進程了oom_adj的一個初始值,接下來的代碼還有針對這個值繼續調整,但是調整的原則就是向下調整,只有判斷出進程的oom_adj的值可以更低的時候才會去改變它。
6). 如果進程有可見的activity的話,那麼將它的adj設置為VISIBLE_APP_ADJ:

if (r.visible) {
    // App has a visible activity; only upgrade adjustment.
    if (adj > ProcessList.VISIBLE_APP_ADJ) {
        adj = ProcessList.VISIBLE_APP_ADJ;
        app.adjType = "visible";
    }
    if (procState > PROCESS_STATE_TOP) {
        procState = PROCESS_STATE_TOP;
    }
    schedGroup = Process.THREAD_GROUP_DEFAULT;
    app.cached = false;
    app.empty = false;
    foregroundActivities = true;
    break;
}

7). 如果進程的activity處於暫停或者正在暫停的情況下,adj調整為PERCEPTIBLE_APP_ADJ:

if (r.state == ActivityState.PAUSING || r.state == ActivityState.PAUSED) {
    if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
        adj = ProcessList.PERCEPTIBLE_APP_ADJ;
        app.adjType = "pausing";
    }
    if (procState > PROCESS_STATE_TOP) {
        procState = PROCESS_STATE_TOP;
    }
    schedGroup = Process.THREAD_GROUP_DEFAULT;
    app.cached = false;
    app.empty = false;
    foregroundActivities = true;
}

8). 如果進程的activity正在處於停止狀態的話,那麼adj設置為PERCEPTIBLE_APP_ADJ:

if (r.state == ActivityState.STOPPING) {
    if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
        adj = ProcessList.PERCEPTIBLE_APP_ADJ;
        app.adjType = "stopping";
    }
    // For the process state, we will at this point consider the
    // process to be cached.  It will be cached either as an activity
    // or empty depending on whether the activity is finishing.  We do
    // this so that we can treat the process as cached for purposes of
    // memory trimming (determing current memory level, trim command to
    // send to process) since there can be an arbitrary number of stopping
    // processes and they should soon all go into the cached state.
    if (!r.finishing) {
        if (procState > ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
            procState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
        }
    }
    app.cached = false;
    app.empty = false;
    foregroundActivities = true;
}

9). 如果adj大於了PERCEPTIBLE_APP_ADJ但是進程中有設置為前台運行的service或者app被設置為前台運行,那麼adj設置為PERCEPTIBLE_APP_ADJ:

if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
  if (app.foregroundServices) {
      // The user is aware of this app, so make it visible.
      adj = ProcessList.PERCEPTIBLE_APP_ADJ;
      procState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
      app.cached = false;
      app.adjType = "fg-service";
      schedGroup = Process.THREAD_GROUP_DEFAULT;
  } else if (app.forcingToForeground != null) {
      // The user is aware of this app, so make it visible.
      adj = ProcessList.PERCEPTIBLE_APP_ADJ;
      procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
      app.cached = false;
      app.adjType = "force-fg";
      app.adjSource = app.forcingToForeground;
      schedGroup = Process.THREAD_GROUP_DEFAULT;
  }
}

10). 如果進程是heavy weight進程的話,將它的adj調整為:HEAVY_WEIGHT_APP_ADJ:

if (app == mHeavyWeightProcess) {
    if (adj > ProcessList.HEAVY_WEIGHT_APP_ADJ) {
        // We don't want to kill the current heavy-weight process.
        adj = ProcessList.HEAVY_WEIGHT_APP_ADJ;
        schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
        app.cached = false;
        app.adjType = "heavy";
    }
    if (procState > ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) {
        procState = ActivityManager.PROCESS_STATE_HEAVY_WEIGHT;
    }
}

這裡所謂的heavy weight進程就是我們傳統的進程,一般的進程都是heavy weight進程,和heavy weight相對的還有light weight,這個就是指的thread。關於heavy weight進程和light weight進程差別詳情請看這個博客(老外寫的):
http://www.wideskills.com/android/intrprocess-communication/threads-in-android
11). 如果當前app進程是home進程,那麼我們將它的adj設置為HOME_APP_ADJ:

if (app == mHomeProcess) {
    if (adj > ProcessList.HOME_APP_ADJ) {
        // This process is hosting what we currently consider to be the
        // home app, so we don't want to let it go into the background.
        adj = ProcessList.HOME_APP_ADJ;
        schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
        app.cached = false;
        app.adjType = "home";
    }
    if (procState > ActivityManager.PROCESS_STATE_HOME) {
        procState = ActivityManager.PROCESS_STATE_HOME;
    }
}

12). 如果這個進程是Previous進程,並且它有activity的話,那麼我們將它的adj設置為PREVIOUS_APP_ADJ:

if (app == mPreviousProcess && app.activities.size() > 0) {
    if (adj > ProcessList.PREVIOUS_APP_ADJ) {
        // This was the previous process that showed UI to the user.
        // We want to try to keep it around more aggressively, to give
        // a good experience around switching between two apps.
        adj = ProcessList.PREVIOUS_APP_ADJ;
        schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
        app.cached = false;
        app.adjType = "previous";
    }
    if (procState > ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
        procState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
    }
}

所謂的previous進程就是當前activity的前一個activity所在的進程,這個進程需要比較高的優先級,因為用戶很有可能會再次回到這個進程中。
13). 如果進程正在備份的話,那麼將它的adj設置為BACKUP_APP_ADJ:

if (mBackupTarget != null && app == mBackupTarget.app) {
    // If possible we want to avoid killing apps while they're being backed up
    if (adj > ProcessList.BACKUP_APP_ADJ) {
        if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "oom BACKUP_APP_ADJ for " + app);
        adj = ProcessList.BACKUP_APP_ADJ;
        if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND) {
            procState = ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND;
        }
        app.adjType = "backup";
        app.cached = false;
    }
    if (procState > ActivityManager.PROCESS_STATE_BACKUP) {
        procState = ActivityManager.PROCESS_STATE_BACKUP;
    }
}

14). 接下來開始處理進程中含有service組件的情況,針對每個service,分兩種情況處理:
a. 如果進程的service是顯式啟動的(service對象的startRequested為true),而且進程還擁有activity,則將它的adj值設置為SERVICE_ADJ
b. 重新計算和service鏈接的其他的進程的adj值,如果service的鏈接標記中包含了BIND_ABOVE_CLIENT的話,那麼將當前進程的adj設置為客戶端的adj值;如果標記中包含了BIND_NOT_VISIBLE則將進程的adj設置為PERCEPTIBLE_APP_ADJ;否則就設置為VISIBLE_APP_ADJ
(這部分代碼太長就不貼出來了,就在上面代碼的下面)
15). 接下來考慮進程中包含ContentProvider的情況,對該進程中的每個provider對象,進行如下處理:
a. 首先計算和ContentProvider鏈接的client進程的oom_adj值,如果當前的oom_adj值大於client的值,那麼就將當前進程的adj設置為和client一樣的值。這表示當前進程的重要性至少和client一樣的。
b. 如果ContentProvider對象有一個應用進程和它鏈接,將當前進程的oom_adj調整為FOREGROUND_APP_ADJ
15). 如果當前進程的adj大於了這個進程可以擁有的最大的adj(maxAdj)的話,那麼就調整為maxAdj

總結

這裡我們大概梳理一下AMS中的進程和oom_adj的管理流程,這是AMS所有的工作中比較重要的,這個是下面理解AMS的activity管理流程基礎,希望本文可以幫助大家更好地理解AMS。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved