Android ActivityManagerService(AMS)的进程管理

Android ActivityManagerService(AMS)的进程管理

标签: androidAMS进程管理内存管理app
1201人阅读 评论(2) 收藏 举报
分类:

目录(?)[+]

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启动进程流程

Android ActivityManagerService(AMS)的启动分析一文中我们提到了AMS是调用addAppLocked方法来启动一个进程的,这个方法实现如下:

<code class="hljs java has-numbering">    <span class="hljs-keyword">final</span> ProcessRecord addAppLocked(ApplicationInfo info, <span class="hljs-keyword">boolean</span> isolated,
            String abiOverride) {
        ProcessRecord app;
        <span class="hljs-comment">// isolated表示当前需要启动的app是不是一个独立的进程,如果是独立的话那就重新</span>
        <span class="hljs-comment">// 创建一个ProcessRecord,如果不是的话那就从正在运行的进程列表中找。</span>
        <span class="hljs-keyword">if</span> (!isolated) {
            app = getProcessRecordLocked(info.processName, info.uid, <span class="hljs-keyword">true</span>);
        } <span class="hljs-keyword">else</span> {
            app = <span class="hljs-keyword">null</span>;
        }

        <span class="hljs-comment">// 这是一个独立的进程或者在正在运行的列表中没有找到相应的记录</span>
        <span class="hljs-keyword">if</span> (app == <span class="hljs-keyword">null</span>) {
            <span class="hljs-comment">// 新建一个ProcessRecord对象</span>
            app = newProcessRecordLocked(info, <span class="hljs-keyword">null</span>, isolated, <span class="hljs-number">0</span>);
            <span class="hljs-comment">// 更新lru列表和oom_adj值,下面我们会重点分析这里</span>
            updateLruProcessLocked(app, <span class="hljs-keyword">false</span>, <span class="hljs-keyword">null</span>);
            updateOomAdjLocked();
        }

        <span class="hljs-comment">// This package really, really can not be stopped.</span>
        <span class="hljs-comment">// 这里将当前的app包设置为启动状态,这样这个app就可以接受系统的隐式intent了</span>
        <span class="hljs-keyword">try</span> {
            AppGlobals.getPackageManager().setPackageStoppedState(
                    info.packageName, <span class="hljs-keyword">false</span>, UserHandle.getUserId(app.uid));
        } <span class="hljs-keyword">catch</span> (RemoteException e) {
        } <span class="hljs-keyword">catch</span> (IllegalArgumentException e) {
            Slog.w(TAG, <span class="hljs-string">"Failed trying to unstop package "</span>
                    + info.packageName + <span class="hljs-string">": "</span> + e);
        }

        <span class="hljs-comment">// 如果app中带有persistent标记的话,那个对新建的app对象做相应的标记</span>
        <span class="hljs-keyword">if</span> ((info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
            app.persistent = <span class="hljs-keyword">true</span>;
            <span class="hljs-comment">// 这个值下面我们分析oom_adj中会说明</span>
            app.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
        }

        <span class="hljs-comment">// 如果app中的thread(就是主线程)为空的话</span>
        <span class="hljs-keyword">if</span> (app.thread == <span class="hljs-keyword">null</span> && mPersistentStartingProcesses.indexOf(app) < <span class="hljs-number">0</span>) {
            mPersistentStartingProcesses.add(app);
            <span class="hljs-comment">// 实际启动app进程</span>
            startProcessLocked(app, <span class="hljs-string">"added application"</span>, app.processName, abiOverride,
                    <span class="hljs-keyword">null</span> <span class="hljs-comment">/* entryPoint */</span>, <span class="hljs-keyword">null</span> <span class="hljs-comment">/* entryPointArgs */</span>);
        }

        <span class="hljs-keyword">return</span> app;
    }</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li></ul>

addAppLocked方法会根据参数isolated来决定这个进程是不是一个独立的进程,如果是那就创建一个新的ProcessRecord对象,如果不是的话,那就调用getProcessRecordLocked方法在当前运行的进程列表中查找进程,我们看下这个方法的定义:
getProcessRecordLocked@ActivityManagerService.java

<code class="hljs mel has-numbering">    final ProcessRecord getProcessRecordLocked(String processName, <span class="hljs-keyword">int</span> uid, boolean keepIfLarge) {
        <span class="hljs-comment">// 如果是一个系统级别的UID,也就是说这个app是由于system用户启动的进程</span>
        <span class="hljs-keyword">if</span> (uid == Process.SYSTEM_UID) {
            <span class="hljs-comment">// The system gets to run in any process.  If there are multiple</span>
            <span class="hljs-comment">// processes with the same uid, just pick the first (this</span>
            <span class="hljs-comment">// should never happen).</span>
            <span class="hljs-comment">// 上面英文注释说的很明白,如果有多个system uid的进程的话,那就取第一个</span>
            SparseArray<ProcessRecord> procs = mProcessNames.getMap().get(processName);
            <span class="hljs-keyword">if</span> (procs == null) <span class="hljs-keyword">return</span> null;
            final <span class="hljs-keyword">int</span> procCount = procs.<span class="hljs-keyword">size</span>();
            <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < procCount; i++) {
                final <span class="hljs-keyword">int</span> procUid = procs.keyAt(i);
                <span class="hljs-comment">// 如果这是一个合理的app进程,或者uid不是要求的uid的话,那就跳过。</span>
                <span class="hljs-comment">// 原因下面的英文注释说的很清楚。</span>
                <span class="hljs-keyword">if</span> (UserHandle.isApp(procUid) || !UserHandle.isSameUser(procUid, uid)) {
                    <span class="hljs-comment">// Don't use an app process or different user process for system component.</span>
                    <span class="hljs-keyword">continue</span>;
                }
                <span class="hljs-keyword">return</span> procs.valueAt(i);
            }
        }

        <span class="hljs-comment">// 如果不是系统uid的话,就会执行到这里。首先从mProcessNames查找正在运行的进程记录</span>
        ProcessRecord <span class="hljs-keyword">proc</span> = mProcessNames.get(processName, uid);
        <span class="hljs-comment">// 上面这个分支永远不会执行,这是一个用户测试的分支。</span>
        <span class="hljs-keyword">if</span> (false && <span class="hljs-keyword">proc</span> != null && !keepIfLarge
                && <span class="hljs-keyword">proc</span>.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY
                && <span class="hljs-keyword">proc</span>.lastCachedPss >= <span class="hljs-number">4000</span>) {
            <span class="hljs-comment">// Turn this condition on to cause killing to happen regularly, for testing.</span>
            <span class="hljs-keyword">if</span> (<span class="hljs-keyword">proc</span>.baseProcessTracker != null) {
                <span class="hljs-keyword">proc</span>.baseProcessTracker.reportCachedKill(<span class="hljs-keyword">proc</span>.pkgList, <span class="hljs-keyword">proc</span>.lastCachedPss);
            }
            <span class="hljs-keyword">proc</span>.kill(Long.toString(<span class="hljs-keyword">proc</span>.lastCachedPss) + <span class="hljs-string">"k from cached"</span>, true);
        <span class="hljs-comment">// 一般会走这个分支,但是由于我们的addAppLocked传递进来的keepIfLarge是true,因此这个分支也不会走。</span>
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">proc</span> != null && !keepIfLarge
                && mLastMemoryLevel > ProcessStats.ADJ_MEM_FACTOR_NORMAL
                && <span class="hljs-keyword">proc</span>.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
            <span class="hljs-keyword">if</span> (DEBUG_PSS) Slog.d(TAG_PSS, <span class="hljs-string">"May not keep "</span> + <span class="hljs-keyword">proc</span> + <span class="hljs-string">": pss="</span> + <span class="hljs-keyword">proc</span>.lastCachedPss);
            <span class="hljs-keyword">if</span> (<span class="hljs-keyword">proc</span>.lastCachedPss >= mProcessList.getCachedRestoreThresholdKb()) {
                <span class="hljs-keyword">if</span> (<span class="hljs-keyword">proc</span>.baseProcessTracker != null) {
                    <span class="hljs-keyword">proc</span>.baseProcessTracker.reportCachedKill(<span class="hljs-keyword">proc</span>.pkgList, <span class="hljs-keyword">proc</span>.lastCachedPss);
                }
                <span class="hljs-keyword">proc</span>.kill(Long.toString(<span class="hljs-keyword">proc</span>.lastCachedPss) + <span class="hljs-string">"k from cached"</span>, true);
            }
        }

        <span class="hljs-comment">// 直接就将找到的记录对象返回。</span>
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">proc</span>;
    }</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li></ul>

getProcessRecordLocked方法的逻辑是比较简单的,他分为两部分:uid是system uid和uid是普通uid两种情况。具体的逻辑上面的注释已经解释,这里就不赘述。需要补充的是,上面如果我们的uid是system uid的话那么会使用UserHandle.isApp来判断这是不是一个app进程,他的实现如下:
isApp@UserHandle.java

<code class="hljs java has-numbering">    <span class="hljs-javadoc">/**<span class="hljs-javadoctag"> @hide</span> */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">isApp</span>(<span class="hljs-keyword">int</span> uid) {
        <span class="hljs-keyword">if</span> (uid > <span class="hljs-number">0</span>) {
            <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> appId = getAppId(uid);
            <span class="hljs-keyword">return</span> appId >= Process.FIRST_APPLICATION_UID && appId <= Process.LAST_APPLICATION_UID;
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
        }
    }</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li></ul>

这里的逻辑很简单,主要就是根据uid获得app的id,然后如果app id是在Process.FIRST_APPLICATION_UID和Process.LAST_APPLICATION_UID之间(这是正常app id应该处于的范围)的话,那就是一个合理的app进程。
现在我们回到addAppLocked方法,我们刚才分析了如果不是独立进程的情况的逻辑,总结来说就是通过getProcessRecordLocked查找当前系统中正在运行的进程记录,并且把这个记录保存下来。现在我们看一下如果请求的是一个独立的进程的话(这也是最常见的情形),处理的方式是什么样的:
addAppLocked@ActivityManagerService.java

<code class="hljs java has-numbering">    <span class="hljs-keyword">final</span> ProcessRecord addAppLocked(ApplicationInfo info, <span class="hljs-keyword">boolean</span> isolated,
            String abiOverride) {
        ProcessRecord app;
        <span class="hljs-keyword">if</span> (!isolated) {
            app = getProcessRecordLocked(info.processName, info.uid, <span class="hljs-keyword">true</span>);
        } <span class="hljs-keyword">else</span> {
            app = <span class="hljs-keyword">null</span>;
        }

        <span class="hljs-keyword">if</span> (app == <span class="hljs-keyword">null</span>) {
            app = newProcessRecordLocked(info, <span class="hljs-keyword">null</span>, isolated, <span class="hljs-number">0</span>);
            updateLruProcessLocked(app, <span class="hljs-keyword">false</span>, <span class="hljs-keyword">null</span>);
            updateOomAdjLocked();
        }</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li></ul>

我们看到,如果是一个独立的进程的话,那么首先app肯定就直接赋值为null,接下来我们会调用newProcessRecordLocked新建一个ProcessRecord对象,这个方法的具体处理因为和我们AMS的进程管理不是特别相关,我们就不分析了,感兴趣的读者可以自行分析。新建了一个ProcessRecord对象之后的操作就是最重要的操作了:

<code class="hljs cs has-numbering">updateLruProcessLocked(app, <span class="hljs-keyword">false</span>, <span class="hljs-keyword">null</span>);
updateOomAdjLocked();</code><ul style="" class="pre-numbering"><li>1</li><li>2</li></ul>

这两步操作设计AMS管理进程的核心工作,我们稍后详细分析,我们先接下来的逻辑,接下来的逻辑中最重要的就是调用startProcessLocked方法实际启动一个进程:

<code class="hljs cs has-numbering">            startProcessLocked(app, <span class="hljs-string">"added application"</span>, app.processName, abiOverride,
                    <span class="hljs-keyword">null</span> <span class="hljs-comment">/* entryPoint */</span>, <span class="hljs-keyword">null</span> <span class="hljs-comment">/* entryPointArgs */</span>);</code><ul style="" class="pre-numbering"><li>1</li><li>2</li></ul>

在AMS中startProcessLocked方法实现了多态,但是根据这里的参数我们可以确定我们调用的方法是哪一个。startProcessLocked方法比较长,这里我们分部来分析:
startProcessLocked@ActivityManagerService.java

<code class="hljs java has-numbering">    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">void</span> <span class="hljs-title">startProcessLocked</span>(ProcessRecord app, String hostingType,
            String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
        <span class="hljs-keyword">long</span> startTime = SystemClock.elapsedRealtime();
        <span class="hljs-keyword">if</span> (app.pid > <span class="hljs-number">0</span> && app.pid != MY_PID) {
            checkTime(startTime, <span class="hljs-string">"startProcess: removing from pids map"</span>);
            <span class="hljs-keyword">synchronized</span> (mPidsSelfLocked) {
                mPidsSelfLocked.remove(app.pid);
                mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
            }
            checkTime(startTime, <span class="hljs-string">"startProcess: done removing from pids map"</span>);
            app.setPid(<span class="hljs-number">0</span>);
        }</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li></ul>

首先需要记录一下app的启动时间,这个数据是给checkTime方法使用的,checkTime方法的定义如下:
checkTime@ActivityManagerService.java

<code class="hljs cs has-numbering">    <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">checkTime</span>(<span class="hljs-keyword">long</span> startTime, String <span class="hljs-keyword">where</span>) {
        <span class="hljs-keyword">long</span> now = SystemClock.elapsedRealtime();
        <span class="hljs-keyword">if</span> ((now-startTime) > <span class="hljs-number">1000</span>) {
            <span class="hljs-comment">// If we are taking more than a second, log about it.</span>
            Slog.w(TAG, <span class="hljs-string">"Slow operation: "</span> + (now-startTime) + <span class="hljs-string">"ms so far, now at "</span> + <span class="hljs-keyword">where</span>);
        }
    }</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li></ul>

我们看到,checkTime的逻辑很简单,主要就是看看当前的时间和传递进来的时间是不是相差1000ms,如果相差1s的话那就需要打下log,这部分的逻辑就是这样。我们继续startProcessLocked的分析:

<code class="hljs avrasm has-numbering">if (app<span class="hljs-preprocessor">.pid</span> > <span class="hljs-number">0</span> && app<span class="hljs-preprocessor">.pid</span> != MY_PID) {
            checkTime(startTime, <span class="hljs-string">"startProcess: removing from pids map"</span>)<span class="hljs-comment">;</span>
            synchronized (mPidsSelfLocked) {
                mPidsSelfLocked<span class="hljs-preprocessor">.remove</span>(app<span class="hljs-preprocessor">.pid</span>)<span class="hljs-comment">;</span>
                mHandler<span class="hljs-preprocessor">.removeMessages</span>(PROC_START_TIMEOUT_MSG, app)<span class="hljs-comment">;</span>
            }
            checkTime(startTime, <span class="hljs-string">"startProcess: done removing from pids map"</span>)<span class="hljs-comment">;</span>
            app<span class="hljs-preprocessor">.setPid</span>(<span class="hljs-number">0</span>)<span class="hljs-comment">;</span>
        }</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li></ul>

再获得启动开始时间之后,当app的pid大于0并且pid不是本进程PID的话,就要把当前pid的进程从mPidsSelfLocked列表中移除,防止重复,因为后面我们还要加入;然后就是移除PROC_START_TIMEOUT_MSG消息,这个消息是AMS用来控制app启动时间的,如果启动超时了就发出效果消息,下面我们会设置这个消息,现在需要取消之前设置的消息,防止干扰。
接下来的逻辑:

<code class="hljs cmake has-numbering">mProcessesOnHold.<span class="hljs-keyword">remove</span>(app);</code><ul style="" class="pre-numbering"><li>1</li></ul>

这里是将app进程从mProcessesOnHold列表中清除,这个列表是什么呢?这个列表是系统中在AMS没有启动之前请求启动的app,这些app当时没有启动,被hold了,当AMS启动完成的时候需要将他们启动;现在如果我们启动的app就在这个列表中的, 那么自然需要将它移除,防止重复启动。我们可以从mProcessesOnHold的定义的地方看到这一点:

<code class="hljs applescript has-numbering">    /**
     * List <span class="hljs-keyword">of</span> records <span class="hljs-keyword">for</span> processes <span class="hljs-keyword">that</span> someone had tried <span class="hljs-keyword">to</span> start <span class="hljs-keyword">before</span> <span class="hljs-keyword">the</span>
     * system was ready.  We don't start them <span class="hljs-keyword">at</span> <span class="hljs-keyword">that</span> point, <span class="hljs-keyword">but</span> ensure they
     * are started <span class="hljs-keyword">by</span> <span class="hljs-keyword">the</span> <span class="hljs-property">time</span> booting <span class="hljs-keyword">is</span> complete.
     */
    final ArrayList<ProcessRecord> mProcessesOnHold = new ArrayList<ProcessRecord>();</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li></ul>

接下来就要更新cpu使用统计信息:

<code class="hljs scss has-numbering"><span class="hljs-function">updateCpuStats()</span>;</code><ul style="" class="pre-numbering"><li>1</li></ul>

cpu统计使用信息也是AMS的一个重要的任务,这个任务就是通过启动一个独立的线程去获得cpu的使用情况,我们看一下updateCpuStats的实现:

<code class="hljs java has-numbering">    <span class="hljs-keyword">void</span> updateCpuStats() {
        <span class="hljs-keyword">final</span> <span class="hljs-keyword">long</span> now = SystemClock.uptimeMillis();
        <span class="hljs-keyword">if</span> (mLastCpuTime.get() >= now - MONITOR_CPU_MIN_TIME) {
            <span class="hljs-keyword">return</span>;
        }
        <span class="hljs-keyword">if</span> (mProcessCpuMutexFree.compareAndSet(<span class="hljs-keyword">true</span>, <span class="hljs-keyword">false</span>)) {
            <span class="hljs-keyword">synchronized</span> (mProcessCpuThread) {
                mProcessCpuThread.notify();
            }
        }
    }</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li></ul>

我们发现它的逻辑很简单,首先是判断当前时间是不是和上次检查时间相差MONITOR_CPU_MIN_TIME(5s)以上,如果是就继续,如果不是就返回,因为不能过于平凡做这件事情,它比较耗电。工作的方式就是唤醒mProcessCpuThread去采集cpu的信息。这个线程的实现我们这里先不分析,这块和我们的进程管理相关不是很密切,我后面的文章会详细分析这块的内容。在startProcessLocked的接下来的逻辑中,主要就是app启动的一些参数设置,条件检查等操作,这里我们直接略过,我们直接看实际进程启动的部分:

<code class="hljs avrasm has-numbering">// Start the process.  It will either succeed <span class="hljs-keyword">and</span> return a result containing
            // the PID of the new process, <span class="hljs-keyword">or</span> else throw a RuntimeException.
            boolean isActivityProcess = (entryPoint == null)<span class="hljs-comment">;</span>
            if (entryPoint == null) entryPoint = <span class="hljs-string">"android.app.ActivityThread"</span><span class="hljs-comment">;</span>
            Trace<span class="hljs-preprocessor">.traceBegin</span>(Trace<span class="hljs-preprocessor">.TRACE</span>_TAG_ACTIVITY_MANAGER, <span class="hljs-string">"Start proc: "</span> +
                    app<span class="hljs-preprocessor">.processName</span>)<span class="hljs-comment">;</span>
            checkTime(startTime, <span class="hljs-string">"startProcess: asking zygote to start proc"</span>)<span class="hljs-comment">;</span>
            // 实际启动了一个进程
            Process<span class="hljs-preprocessor">.ProcessStartResult</span> startResult = Process<span class="hljs-preprocessor">.start</span>(entryPoint,
                    app<span class="hljs-preprocessor">.processName</span>, uid, uid, gids, debugFlags, mountExternal,
                    app<span class="hljs-preprocessor">.info</span><span class="hljs-preprocessor">.targetSdkVersion</span>, app<span class="hljs-preprocessor">.info</span><span class="hljs-preprocessor">.seinfo</span>, requiredAbi, instructionSet,
                    app<span class="hljs-preprocessor">.info</span><span class="hljs-preprocessor">.dataDir</span>, entryPointArgs)<span class="hljs-comment">;</span>
            checkTime(startTime, <span class="hljs-string">"startProcess: returned from zygote!"</span>)<span class="hljs-comment">;</span>
            Trace<span class="hljs-preprocessor">.traceEnd</span>(Trace<span class="hljs-preprocessor">.TRACE</span>_TAG_ACTIVITY_MANAGER)<span class="hljs-comment">;</span></code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li></ul>

上面代码中最核心的代码就是调用Process.start静态方法部分,这个调用会通过socket和zygote建立链接请求,fork新进程,我们看一下start方法实现:

<code class="hljs java has-numbering">    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> ProcessStartResult <span class="hljs-title">start</span>(<span class="hljs-keyword">final</span> String processClass,
                                  <span class="hljs-keyword">final</span> String niceName,
                                  <span class="hljs-keyword">int</span> uid, <span class="hljs-keyword">int</span> gid, <span class="hljs-keyword">int</span>[] gids,
                                  <span class="hljs-keyword">int</span> debugFlags, <span class="hljs-keyword">int</span> mountExternal,
                                  <span class="hljs-keyword">int</span> targetSdkVersion,
                                  String seInfo,
                                  String abi,
                                  String instructionSet,
                                  String appDataDir,
                                  String[] zygoteArgs) {
        <span class="hljs-keyword">try</span> {
            <span class="hljs-keyword">return</span> startViaZygote(processClass, niceName, uid, gid, gids,
                    debugFlags, mountExternal, targetSdkVersion, seInfo,
                    abi, instructionSet, appDataDir, zygoteArgs);
        } <span class="hljs-keyword">catch</span> (ZygoteStartFailedEx ex) {
            Log.e(LOG_TAG,
                    <span class="hljs-string">"Starting VM process through Zygote failed"</span>);
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> RuntimeException(
                    <span class="hljs-string">"Starting VM process through Zygote failed"</span>, ex);
        }
    }</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li></ul>

这里的startViaZygote方法会和zygote建立链接并且创建进程,这里我们就不详细分析zygote部分的fork逻辑了。我们再次回到startProcessLocked方法中分析剩下的逻辑,刚才我们上面的请求zygote的部分是一个异步请求,会立即返回,当时实际进程启动需要多长的时间是不确定的,但是我们不能无限制等待,需要有一个启动超时机制,在startProcessLocked接下来的逻辑中就启动了一个延迟消息:

<code class="hljs avrasm has-numbering">synchronized (mPidsSelfLocked) {
                this<span class="hljs-preprocessor">.mPidsSelfLocked</span><span class="hljs-preprocessor">.put</span>(startResult<span class="hljs-preprocessor">.pid</span>, app)<span class="hljs-comment">;</span>
                if (isActivityProcess) {
                    Message msg = mHandler<span class="hljs-preprocessor">.obtainMessage</span>(PROC_START_TIMEOUT_MSG)<span class="hljs-comment">;</span>
                    msg<span class="hljs-preprocessor">.obj</span> = app<span class="hljs-comment">;</span>
                    mHandler<span class="hljs-preprocessor">.sendMessageDelayed</span>(msg, startResult<span class="hljs-preprocessor">.usingWrapper</span>
                            ? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT)<span class="hljs-comment">;</span>
                }
            }</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li></ul>

这个消息是PROC_START_TIMEOUT_MSG用于通知应用启动超时了,由mHandler负责处理。并且超时的阀值是根据当前启动的进程是不是一个wrapper进程来决定的,如果是wrapper进程的话那超时就是PROC_START_TIMEOUT_WITH_WRAPPER(1200s),如果不是则PROC_START_TIMEOUT(10s),正常情况下都是后者,wrapper进程使用场景较少。为了弄清楚假如发生了启动超时,系统怎么处理,我们还需要看一下mHandler对这个消息的处理:
mHandler@ActivityManagerService.java

<code class="hljs java has-numbering"><span class="hljs-keyword">case</span> PROC_START_TIMEOUT_MSG: {
                <span class="hljs-keyword">if</span> (mDidDexOpt) {
                    mDidDexOpt = <span class="hljs-keyword">false</span>;
                    Message nmsg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
                    nmsg.obj = msg.obj;
                    mHandler.sendMessageDelayed(nmsg, PROC_START_TIMEOUT);
                    <span class="hljs-keyword">return</span>;
                }
                ProcessRecord app = (ProcessRecord)msg.obj;
                <span class="hljs-keyword">synchronized</span> (ActivityManagerService.<span class="hljs-keyword">this</span>) {
                    processStartTimedOutLocked(app);
                }
            } <span class="hljs-keyword">break</span>;</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li></ul>

首先需要判断当前这个app是不处在dex优化过程中,如果是处在dex优化过程中的话,那么app的启动时间很长可以理解,因为实际的启动需要等到dex优化完毕才能进行,这个时候就直接再等PROC_START_TIMEOUT(10s)之后再检查。我们看下mDidDexOpt的定义就知道了:

<code class="hljs applescript has-numbering">    /**
     * This <span class="hljs-keyword">is</span> <span class="hljs-keyword">set</span> <span class="hljs-keyword">if</span> we had <span class="hljs-keyword">to</span> do a delayed dexopt <span class="hljs-keyword">of</span> an app <span class="hljs-keyword">before</span> launching
     * <span class="hljs-keyword">it</span>, <span class="hljs-keyword">to</span> increase <span class="hljs-keyword">the</span> ANR timeouts <span class="hljs-keyword">in</span> <span class="hljs-keyword">that</span> case.
     */
    <span class="hljs-type">boolean</span> mDidDexOpt;</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li></ul>

这里的注释写的很清楚了。在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管理机制之前,我们需要关注以下两个变量:

<code class="hljs java has-numbering">    <span class="hljs-javadoc">/**
     * Where in mLruProcesses that the processes hosting activities start.
     */</span>
    <span class="hljs-keyword">int</span> mLruProcessActivityStart = <span class="hljs-number">0</span>;

    <span class="hljs-javadoc">/**
     * Where in mLruProcesses that the processes hosting services start.
     * This is after (lower index) than mLruProcessesActivityStart.
     */</span>
    <span class="hljs-keyword">int</span> mLruProcessServiceStart = <span class="hljs-number">0</span>;</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li></ul>

就如同注释解释的那样,第一个变量总是指向列表中位置最高的带有activity进程和没有activity只有service的进程,并且mLruProcessServiceStart总是在mLruProcessActivityStart的后面。还需要注意的是mLruProcesses的定义:

<code class="hljs java has-numbering">    <span class="hljs-javadoc">/**
     * List of running applications, sorted by recent usage.
     * The first entry in the list is the least recently used.
     */</span>
    <span class="hljs-keyword">final</span> ArrayList<ProcessRecord> mLruProcesses = <span class="hljs-keyword">new</span> ArrayList<ProcessRecord>();</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li></ul>

我们看到mLruProcesses只是一个ArrayList,在mLruProcesses中,某个成员的index越大,就表示这个app越是最近使用的,这一点在后面的分析中很重要。
接下来我们看一下updateLruProcessLocked的实现,这个方法比较长,我们分段来分析这个方法的实现:

<code class="hljs axapta has-numbering">    <span class="hljs-keyword">final</span> <span class="hljs-keyword">void</span> updateLruProcessLocked(ProcessRecord app, <span class="hljs-keyword">boolean</span> activityChange,
            ProcessRecord <span class="hljs-keyword">client</span>) {
        <span class="hljs-keyword">final</span> <span class="hljs-keyword">boolean</span> hasActivity = app.activities.size() > <span class="hljs-number">0</span> || app.hasClientActivities
                || app.treatLikeActivity;
        <span class="hljs-keyword">final</span> <span class="hljs-keyword">boolean</span> hasService = <span class="hljs-keyword">false</span>; <span class="hljs-comment">// not impl yet. app.services.size() > 0;</span></code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li></ul>

首先我们看到这里定义一个变量hasActivity用来表示某个app中是否包含activity组件,以下三种情况都可以看做是有activity组件的:app本身确实包含activity组件;app本身有service,并且有另外一个含有activity的app链接到此app的service上;该app启动serivce的时候带有标记BIND_TREAT_LIKE_ACTIVITY。接下来的变量hasService只是定义了,但是没有实际的实现,从后面的注释我们也能看出这一点(Google的代码还是有待开发的,有很多功能还是没有完善的,android系统源码中有很多这样的代码),所以我们后面分析的时候和这个变量相关的代码可以直接跳过。接下来的代码如下:

<code class="hljs cs has-numbering"><span class="hljs-keyword">if</span> (!activityChange && hasActivity) {
            <span class="hljs-comment">// The process has activities, so we are only allowing activity-based adjustments</span>
            <span class="hljs-comment">// to move it.  It should be kept in the front of the list with other</span>
            <span class="hljs-comment">// processes that have activities, and we don't want those to change their</span>
            <span class="hljs-comment">// order except due to activity operations.</span>
            <span class="hljs-keyword">return</span>;
        }</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li></ul>

这里的代码中注释写的很清楚,这里的意思就是,如果当前app中有activity组件,并且不是activity发生了改变(启动或者销毁等),那就直接返回,因为这个时候没有必要更新LRU。
我们继续看下面的代码:

<code class="hljs cs has-numbering">        <span class="hljs-comment">// 这是系统中的一个计数器,记录本方法被调用了多少次,也就是LRU更新了多少次。</span>
        mLruSeq++;
        <span class="hljs-comment">// 记录更新时间</span>
        final <span class="hljs-keyword">long</span> now = SystemClock.uptimeMillis();
        app.lastActivityTime = now;

        <span class="hljs-comment">// First a quick reject: if the app is already at the position we will</span>
        <span class="hljs-comment">// put it, then there is nothing to do.</span>
        <span class="hljs-comment">// 上面英文注释写的很清楚,就是查看我们要插入的app是不是已经在mLruProcesses顶端了,如果是就不用插入了。</span>
        <span class="hljs-keyword">if</span> (hasActivity) {
            <span class="hljs-comment">// 有activity的情况</span>
            final <span class="hljs-keyword">int</span> N = mLruProcesses.size();
            <span class="hljs-keyword">if</span> (N > <span class="hljs-number">0</span> && mLruProcesses.<span class="hljs-keyword">get</span>(N-<span class="hljs-number">1</span>) == app) {
                <span class="hljs-keyword">if</span> (DEBUG_LRU) Slog.d(TAG_LRU, <span class="hljs-string">"Not moving, already top activity: "</span> + app);
                <span class="hljs-keyword">return</span>;
            }
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-comment">// 没有activity情况,可以暂时理解为有service的情况(真正有service的判定暂时没有实现呢!!)</span>
            <span class="hljs-keyword">if</span> (mLruProcessServiceStart > <span class="hljs-number">0</span>
                    && mLruProcesses.<span class="hljs-keyword">get</span>(mLruProcessServiceStart-<span class="hljs-number">1</span>) == app) {
                <span class="hljs-keyword">if</span> (DEBUG_LRU) Slog.d(TAG_LRU, <span class="hljs-string">"Not moving, already top other: "</span> + app);
                <span class="hljs-keyword">return</span>;
            }
        }</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li></ul>

上面的代码意图我的注释中已经写清楚了,基本逻辑也很简单。下面我们继续分析代码:

<code class="hljs cs has-numbering">        <span class="hljs-keyword">int</span> lrui = mLruProcesses.lastIndexOf(app);

        <span class="hljs-keyword">if</span> (app.persistent && lrui >= <span class="hljs-number">0</span>) {
            <span class="hljs-comment">// We don't care about the position of persistent processes, as long as</span>
            <span class="hljs-comment">// they are in the list.</span>
            <span class="hljs-keyword">if</span> (DEBUG_LRU) Slog.d(TAG_LRU, <span class="hljs-string">"Not moving, persistent: "</span> + app);
            <span class="hljs-keyword">return</span>;
        }</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li></ul>

这部分的代码主要就是要排除persistent app,这部分app不会被杀死,永远在运行,因此没有必要针对这样的app进程进行LRU排序。

<code class="hljs lua has-numbering">        <span class="hljs-keyword">if</span> (lrui >= <span class="hljs-number">0</span>) {
            <span class="hljs-keyword">if</span> (lrui < mLruProcessActivityStart) {
                mLruProcessActivityStart<span class="hljs-comment">--;</span>
            }
            <span class="hljs-keyword">if</span> (lrui < mLruProcessServiceStart) {
                mLruProcessServiceStart<span class="hljs-comment">--;</span>
            }
            mLruProcesses.remove(lrui);
        }</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li></ul>

这部分代码逻辑也很简单,首先判断一下我们需要更新的app所在的index,如果小于mLruProcessActivityStart,那么mLruProcessActivityStart需要减1,同理如果小于mLruProcessServiceStart,那么mLruProcessServiceStart也需要减1,这两个变量的含义上面已经解释,这里操作因为我们下面即将remove掉这个app进程记录(我们要把它放到合适的地方去),所以这里都要减1.
接下来的代码就是主要操作代码了,分为3种情况操作:有activity的,有service的(没有实现,我们略过),其他的(暂时可以理解为有service的)。我们首先看一下有activity的情况:

<code class="hljs cs has-numbering">        <span class="hljs-comment">// 这个变量指向我们操作app进程记录的下一个index</span>
        <span class="hljs-keyword">int</span> nextIndex;
        <span class="hljs-keyword">if</span> (hasActivity) {
            final <span class="hljs-keyword">int</span> N = mLruProcesses.size();
            <span class="hljs-comment">// 这个分支表示目前app没有activity,并且链接到它的service中的app有activity,这里就把这个app添加到mLruProcesses的第二位,因为第一位必须是实际包含activity的app进程</span>
            <span class="hljs-keyword">if</span> (app.activities.size() == <span class="hljs-number">0</span> && mLruProcessActivityStart < (N - <span class="hljs-number">1</span>)) {
                <span class="hljs-comment">// Process doesn't have activities, but has clients with</span>
                <span class="hljs-comment">// activities...  move it up, but one below the top (the top</span>
                <span class="hljs-comment">// should always have a real activity).</span>
                <span class="hljs-keyword">if</span> (DEBUG_LRU) Slog.d(TAG_LRU,
                        <span class="hljs-string">"Adding to second-top of LRU activity list: "</span> + app);
                mLruProcesses.add(N - <span class="hljs-number">1</span>, app);
                <span class="hljs-comment">// To keep it from spamming the LRU list (by making a bunch of clients),</span>
                <span class="hljs-comment">// we will push down any other entries owned by the app.</span>
                <span class="hljs-comment">// 为了防止某个app中的service绑定了一群client从而导致LRU中顶部大部分都是这些client,这里需要将这些client往下移动,以防止某些app通过和某个app的service绑定从而提升自己在LRU中位置。</span>
                final <span class="hljs-keyword">int</span> uid = app.info.uid;
                <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = N - <span class="hljs-number">2</span>; i > mLruProcessActivityStart; i--) {
                    ProcessRecord subProc = mLruProcesses.<span class="hljs-keyword">get</span>(i);
                    <span class="hljs-keyword">if</span> (subProc.info.uid == uid) {
                        <span class="hljs-comment">// We want to push this one down the list.  If the process after</span>
                        <span class="hljs-comment">// it is for the same uid, however, don't do so, because we don't</span>
                        <span class="hljs-comment">// want them internally to be re-ordered.</span>
                        <span class="hljs-keyword">if</span> (mLruProcesses.<span class="hljs-keyword">get</span>(i - <span class="hljs-number">1</span>).info.uid != uid) {
                            <span class="hljs-keyword">if</span> (DEBUG_LRU) Slog.d(TAG_LRU,
                                    <span class="hljs-string">"Pushing uid "</span> + uid + <span class="hljs-string">" swapping at "</span> + i + <span class="hljs-string">": "</span>
                                    + mLruProcesses.<span class="hljs-keyword">get</span>(i) + <span class="hljs-string">" : "</span> + mLruProcesses.<span class="hljs-keyword">get</span>(i - <span class="hljs-number">1</span>));
                            ProcessRecord tmp = mLruProcesses.<span class="hljs-keyword">get</span>(i);
                            mLruProcesses.<span class="hljs-keyword">set</span>(i, mLruProcesses.<span class="hljs-keyword">get</span>(i - <span class="hljs-number">1</span>));
                            mLruProcesses.<span class="hljs-keyword">set</span>(i - <span class="hljs-number">1</span>, tmp);
                            i--;
                        }
                    } <span class="hljs-keyword">else</span> {
                        <span class="hljs-comment">// A gap, we can stop here.</span>
                        <span class="hljs-keyword">break</span>;
                    }
                }
            <span class="hljs-comment">// 如果进程有activity,就把它放到顶端(很多情况下都是走这个逻辑)</span>
            } <span class="hljs-keyword">else</span> {
                <span class="hljs-comment">// Process has activities, put it at the very tipsy-top.</span>
                <span class="hljs-keyword">if</span> (DEBUG_LRU) Slog.d(TAG_LRU, <span class="hljs-string">"Adding to top of LRU activity list: "</span> + app);
                mLruProcesses.add(app);
            }
            nextIndex = mLruProcessServiceStart;
        }</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li></ul>

上面的代码展示了当某个应用的hasActivity为true的时候的逻辑,上面的注释说的很清楚,这里不再赘述,只是读者需要仔细理解一下这里的代码,才能理解其中的含义。我们继续看接下来的代码:

<code class="hljs axapta has-numbering">        <span class="hljs-comment">// 这个分支不会执行,因为hasService判定没有实现,总是为false</span>
        <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (hasService) {
            <span class="hljs-comment">// Process has services, put it at the top of the service list.</span>
            <span class="hljs-keyword">if</span> (DEBUG_LRU) Slog.d(TAG_LRU, <span class="hljs-string">"Adding to top of LRU service list: "</span> + app);
            mLruProcesses.add(mLruProcessActivityStart, app);
            nextIndex = mLruProcessServiceStart;
            mLruProcessActivityStart++;
        <span class="hljs-comment">// 一般就是走这个分支了,目前可以理解为有service的情况</span>
        } <span class="hljs-keyword">else</span>  {
            <span class="hljs-comment">// Process not otherwise of interest, it goes to the top of the non-service area.</span>
            <span class="hljs-comment">// 我们直接定义index为mLruProcessServiceStart,因为此时的位置最高只能是mLruProcessServiceStart这个位置</span>
            <span class="hljs-keyword">int</span> <span class="hljs-keyword">index</span> = mLruProcessServiceStart;
            <span class="hljs-comment">// 一般情况下client都是null</span>
            <span class="hljs-keyword">if</span> (<span class="hljs-keyword">client</span> != <span class="hljs-keyword">null</span>) {
                <span class="hljs-comment">// If there is a client, don't allow the process to be moved up higher</span>
                <span class="hljs-comment">// in the list than that client.</span>
                <span class="hljs-keyword">int</span> clientIndex = mLruProcesses.lastIndexOf(<span class="hljs-keyword">client</span>);
                <span class="hljs-keyword">if</span> (DEBUG_LRU && clientIndex < <span class="hljs-number">0</span>) Slog.d(TAG_LRU, <span class="hljs-string">"Unknown client "</span> + <span class="hljs-keyword">client</span>
                        + <span class="hljs-string">" when updating "</span> + app);
                <span class="hljs-keyword">if</span> (clientIndex <= lrui) {
                    <span class="hljs-comment">// Don't allow the client index restriction to push it down farther in the</span>
                    <span class="hljs-comment">// list than it already is.</span>
                    clientIndex = lrui;
                }
                <span class="hljs-keyword">if</span> (clientIndex >= <span class="hljs-number">0</span> && <span class="hljs-keyword">index</span> > clientIndex) {
                    <span class="hljs-keyword">index</span> = clientIndex;
                }
            }
            <span class="hljs-keyword">if</span> (DEBUG_LRU) Slog.d(TAG_LRU, <span class="hljs-string">"Adding at "</span> + <span class="hljs-keyword">index</span> + <span class="hljs-string">" of LRU list: "</span> + app);
            <span class="hljs-comment">// 将app进程记录对象插入到index位置</span>
            mLruProcesses.add(<span class="hljs-keyword">index</span>, app);
            nextIndex = <span class="hljs-keyword">index</span>-<span class="hljs-number">1</span>;
            mLruProcessActivityStart++;
            mLruProcessServiceStart++;
        }</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li></ul>

到这里我们的LRU基本就更新完毕,下面的代码对这个列表还需要进行一定的微调:

<code class="hljs avrasm has-numbering">        // If the app is currently using a content provider <span class="hljs-keyword">or</span> service,
        // bump those processes as well.
        for (int j=app<span class="hljs-preprocessor">.connections</span><span class="hljs-preprocessor">.size</span>()-<span class="hljs-number">1</span><span class="hljs-comment">; j>=0; j--) {</span>
            ConnectionRecord cr = app<span class="hljs-preprocessor">.connections</span><span class="hljs-preprocessor">.valueAt</span>(j)<span class="hljs-comment">;</span>
            if (cr<span class="hljs-preprocessor">.binding</span> != null && !cr<span class="hljs-preprocessor">.serviceDead</span> && cr<span class="hljs-preprocessor">.binding</span><span class="hljs-preprocessor">.service</span> != null
                    && cr<span class="hljs-preprocessor">.binding</span><span class="hljs-preprocessor">.service</span><span class="hljs-preprocessor">.app</span> != null
                    && cr<span class="hljs-preprocessor">.binding</span><span class="hljs-preprocessor">.service</span><span class="hljs-preprocessor">.app</span><span class="hljs-preprocessor">.lruSeq</span> != mLruSeq
                    && !cr<span class="hljs-preprocessor">.binding</span><span class="hljs-preprocessor">.service</span><span class="hljs-preprocessor">.app</span><span class="hljs-preprocessor">.persistent</span>) {
                nextIndex = updateLruProcessInternalLocked(cr<span class="hljs-preprocessor">.binding</span><span class="hljs-preprocessor">.service</span><span class="hljs-preprocessor">.app</span>, now, nextIndex,
                        <span class="hljs-string">"service connection"</span>, cr, app)<span class="hljs-comment">;</span>
            }
        }
        for (int j=app<span class="hljs-preprocessor">.conProviders</span><span class="hljs-preprocessor">.size</span>()-<span class="hljs-number">1</span><span class="hljs-comment">; j>=0; j--) {</span>
            ContentProviderRecord cpr = app<span class="hljs-preprocessor">.conProviders</span><span class="hljs-preprocessor">.get</span>(j)<span class="hljs-preprocessor">.provider</span><span class="hljs-comment">;</span>
            if (cpr<span class="hljs-preprocessor">.proc</span> != null && cpr<span class="hljs-preprocessor">.proc</span><span class="hljs-preprocessor">.lruSeq</span> != mLruSeq && !cpr<span class="hljs-preprocessor">.proc</span><span class="hljs-preprocessor">.persistent</span>) {
                nextIndex = updateLruProcessInternalLocked(cpr<span class="hljs-preprocessor">.proc</span>, now, nextIndex,
                        <span class="hljs-string">"provider reference"</span>, cpr, app)<span class="hljs-comment">;</span>
            }
        }</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li></ul>

这里的微调分为两种情况,第一是将和本进程的service关联的client进程的位置调整到本进程之后,第二是将和本进程ContentProvider关联的client进程位置调整到本进程之后。调整的方式都是使用updateLruProcessInternalLocked方法,我们看一下updateLruProcessInternalLocked方法的定义:
updateLruProcessInternalLocked@ActivityManagerService.java

<code class="hljs axapta has-numbering">    <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> updateLruProcessInternalLocked(ProcessRecord app, <span class="hljs-keyword">long</span> now, <span class="hljs-keyword">int</span> <span class="hljs-keyword">index</span>,
            String what, Object obj, ProcessRecord srcApp) {
        app.lastActivityTime = now;

        <span class="hljs-comment">// 如果有activity,不做调整,因为不属于这个调整范围</span>
        <span class="hljs-keyword">if</span> (app.activities.size() > <span class="hljs-number">0</span>) {
            <span class="hljs-comment">// Don't want to touch dependent processes that are hosting activities.</span>
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">index</span>;
        }

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

        <span class="hljs-comment">// 如果目前进程的位置高于需要调整的位置,那么不做调整,返回</span>
        <span class="hljs-keyword">if</span> (lrui >= <span class="hljs-keyword">index</span>) {
            <span class="hljs-comment">// Don't want to cause this to move dependent processes *back* in the</span>
            <span class="hljs-comment">// list as if they were less frequently used.</span>
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">index</span>;
        }

        <span class="hljs-comment">// 如果目前进程的位置比mLruProcessActivityStart还要高,无需调整</span>
        <span class="hljs-keyword">if</span> (lrui >= mLruProcessActivityStart) {
            <span class="hljs-comment">// Don't want to touch dependent processes that are hosting activities.</span>
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">index</span>;
        }

        <span class="hljs-comment">// 把app调整到index-1的位置</span>
        mLruProcesses.remove(lrui);
        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">index</span> > <span class="hljs-number">0</span>) {
            <span class="hljs-keyword">index</span>--;
        }
        <span class="hljs-keyword">if</span> (DEBUG_LRU) Slog.d(TAG_LRU, <span class="hljs-string">"Moving dep from "</span> + lrui + <span class="hljs-string">" to "</span> + <span class="hljs-keyword">index</span>
                + <span class="hljs-string">" in LRU list: "</span> + app);
        mLruProcesses.add(<span class="hljs-keyword">index</span>, app);
        <span class="hljs-comment">// 返回目前进程的位置</span>
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">index</span>;
    }</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li></ul>

我们前面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中:

<code class="hljs vhdl has-numbering">    // OOM adjustments <span class="hljs-keyword">for</span> processes <span class="hljs-keyword">in</span> various states:

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

    // This <span class="hljs-keyword">is</span> a <span class="hljs-keyword">process</span> only hosting activities that are <span class="hljs-keyword">not</span> visible,
    // so it can be killed without any disruption.
    static final int CACHED_APP_MAX_ADJ = <span class="hljs-number">15</span>;
    static final int CACHED_APP_MIN_ADJ = <span class="hljs-number">9</span>;

    // The B list <span class="hljs-keyword">of</span> SERVICE_ADJ <span class="hljs-comment">-- these are the old and decrepit</span>
    // services that aren<span class="hljs-attribute">'t</span> as shiny <span class="hljs-keyword">and</span> interesting as the ones <span class="hljs-keyword">in</span> the A list.
    static final int SERVICE_B_ADJ = <span class="hljs-number">8</span>;

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

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

    // This <span class="hljs-keyword">is</span> a <span class="hljs-keyword">process</span> holding an application service <span class="hljs-comment">-- killing it will not</span>
    // have much <span class="hljs-keyword">of</span> an impact as far as the user <span class="hljs-keyword">is</span> concerned.
    static final int SERVICE_ADJ = <span class="hljs-number">5</span>;

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

    // This <span class="hljs-keyword">is</span> a <span class="hljs-keyword">process</span> currently hosting a backup operation.  Killing it
    // <span class="hljs-keyword">is</span> <span class="hljs-keyword">not</span> entirely fatal but <span class="hljs-keyword">is</span> generally a bad idea.
    static final int BACKUP_APP_ADJ = <span class="hljs-number">3</span>;

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

    // This <span class="hljs-keyword">is</span> a <span class="hljs-keyword">process</span> only hosting activities that are visible <span class="hljs-keyword">to</span> the
    // user, so we<span class="hljs-attribute">'d</span> prefer they don<span class="hljs-attribute">'t</span> disappear.
    static final int VISIBLE_APP_ADJ = <span class="hljs-number">1</span>;

    // This <span class="hljs-keyword">is</span> the <span class="hljs-keyword">process</span> running the current foreground app.  We<span class="hljs-attribute">'d</span> really
    // rather <span class="hljs-keyword">not</span> kill it!
    static final int FOREGROUND_APP_ADJ = <span class="hljs-number">0</span>;

    // This <span class="hljs-keyword">is</span> a <span class="hljs-keyword">process</span> that the system <span class="hljs-keyword">or</span> a persistent <span class="hljs-keyword">process</span> has bound <span class="hljs-keyword">to</span>,
    // <span class="hljs-keyword">and</span> indicated it <span class="hljs-keyword">is</span> important.
    static final int PERSISTENT_SERVICE_ADJ = -<span class="hljs-number">11</span>;

    // This <span class="hljs-keyword">is</span> a system persistent <span class="hljs-keyword">process</span>, such as telephony.  Definitely
    // don<span class="hljs-attribute">'t</span> want <span class="hljs-keyword">to</span> kill it, but doing so <span class="hljs-keyword">is</span> <span class="hljs-keyword">not</span> completely fatal.
    static final int PERSISTENT_PROC_ADJ = -<span class="hljs-number">12</span>;

    // The system <span class="hljs-keyword">process</span> runs at the <span class="hljs-keyword">default</span> adjustment.
    static final int SYSTEM_ADJ = -<span class="hljs-number">16</span>;

    // Special code <span class="hljs-keyword">for</span> native processes that are <span class="hljs-keyword">not</span> being managed by the system (so
    // don<span class="hljs-attribute">'t</span> have an oom adj assigned by the system).
    static final int NATIVE_ADJ = -<span class="hljs-number">17</span>;</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li><li>53</li><li>54</li><li>55</li><li>56</li><li>57</li><li>58</li><li>59</li><li>60</li><li>61</li><li>62</li><li>63</li><li>64</li><li>65</li><li>66</li><li>67</li><li>68</li><li>69</li></ul>

这里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方法完成,这个方法比较长,我们分段来分析:
updateOomAdjLocked@ActivityManagerService.java

<code class="hljs java has-numbering">    <span class="hljs-keyword">final</span> <span class="hljs-keyword">void</span> updateOomAdjLocked() {
        <span class="hljs-keyword">final</span> ActivityRecord TOP_ACT = resumedAppLocked();
        <span class="hljs-keyword">final</span> ProcessRecord TOP_APP = TOP_ACT != <span class="hljs-keyword">null</span> ? TOP_ACT.app : <span class="hljs-keyword">null</span>;
        <span class="hljs-keyword">final</span> <span class="hljs-keyword">long</span> now = SystemClock.uptimeMillis();
        <span class="hljs-keyword">final</span> <span class="hljs-keyword">long</span> oldTime = now - ProcessList.MAX_EMPTY_TIME;
        <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> N = mLruProcesses.size();</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li></ul>

这里是一些变量的初始化,比较重要的是这里首先获得了位于前台的activity所在的进程,通过resumedAppLocked方法获得,resumedAppLocked会通过ActivityStackSupervisor.java中的resumedAppLocked来获取,这部分涉及AMS的activity的管理,我们先不分析,后面我们分析AMS的activity的管理的时候我们再说明这一块。这里还要注意的是oldTime,oldTime是用当前时间减去MAX_EMPTY_TIME获得的,MAX_EMPTY_TIME的定义如下:

<code class="hljs java has-numbering">    <span class="hljs-comment">// We allow empty processes to stick around for at most 30 minutes.</span>
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">long</span> MAX_EMPTY_TIME = <span class="hljs-number">30</span>*<span class="hljs-number">60</span>*<span class="hljs-number">1000</span>;</code><ul style="" class="pre-numbering"><li>1</li><li>2</li></ul>

它的长度是30min,从上面的注释我们也可以看到,所有的空进程的保留时间都是30min,30min后就会杀死它,后面我们会看到这个操作。
我们继续updateOomAdjLocked的分析:

<code class="hljs java has-numbering">        <span class="hljs-comment">// Reset state in all uid records.</span>
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i=mActiveUids.size()-<span class="hljs-number">1</span>; i>=<span class="hljs-number">0</span>; i--) {
            <span class="hljs-keyword">final</span> UidRecord uidRec = mActiveUids.valueAt(i);
            <span class="hljs-keyword">if</span> (<span class="hljs-keyword">false</span> && DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
                    <span class="hljs-string">"Starting update of "</span> + uidRec);
            uidRec.reset();
        }</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li></ul>

接下来就是将mActiveUids中的每一个UidRecord对象中的curProcState都重置为PROCESS_STATE_CACHED_EMPTY:
reset@UidRecord.java

<code class="hljs cs has-numbering">    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">reset</span>() {
        curProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
    }</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li></ul>

UidRecord是用来表示一个uid启动的进程记录,而AMS中的mActiveUids是一个SparseArray,存储了每一个uid的对应的最新运行的进程,我们看下mActiveUids定义就知道了:

<code class="hljs java has-numbering">    <span class="hljs-javadoc">/**
     * Track all uids that have actively running processes.
     */</span>
    <span class="hljs-keyword">final</span> SparseArray<UidRecord> mActiveUids = <span class="hljs-keyword">new</span> SparseArray<>();</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li></ul>

这里的注释说的很清楚了。返回到updateOomAdjLocked中,我们把mActiveUids每一个UidRecord的当前进程状态reset为PROCESS_STATE_CACHED_EMPTY,是因为我们下面需要给他一个合适的值。
我们继续查看updateOomAdjLocked接下来的代码:

<code class="hljs fix has-numbering"><span class="hljs-attribute">        mAdjSeq++;
        mNewNumServiceProcs </span>=<span class="hljs-string"> 0;
        mNewNumAServiceProcs = 0;</span></code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li></ul>

首先更新oom_adj更新计数,然后将和service process有关的两个变量赋值为0,后面的代码会操作这个变量,我们到时候再讲解。
接下来,就是获得系统后台进程限制的情况了:

<code class="hljs java has-numbering">        <span class="hljs-comment">// 表示后台可以运行的空进程数量</span>
        <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> emptyProcessLimit;
        <span class="hljs-comment">// 表示后台可以运行的缓存进程的数量</span>
        <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> cachedProcessLimit;
        <span class="hljs-comment">// mProcessLimit变量表示系统中一共可以有多少个后台进程(empty + cache)</span>
        <span class="hljs-keyword">if</span> (mProcessLimit <= <span class="hljs-number">0</span>) {
            emptyProcessLimit = cachedProcessLimit = <span class="hljs-number">0</span>;
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (mProcessLimit == <span class="hljs-number">1</span>) {
            emptyProcessLimit = <span class="hljs-number">1</span>;
            cachedProcessLimit = <span class="hljs-number">0</span>;
        } <span class="hljs-keyword">else</span> {
            emptyProcessLimit = ProcessList.computeEmptyProcessLimit(mProcessLimit);
            cachedProcessLimit = mProcessLimit - emptyProcessLimit;
        }</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li></ul>

上面的代码就是获得系统有关后台进程数目的限制设置,系统中后台进程总数目不能超过mProcessLimit个,这个变量的初始值是:

<code class="hljs fix has-numbering"><span class="hljs-attribute">    int mProcessLimit </span>=<span class="hljs-string"> ProcessList.MAX_CACHED_APPS;</span></code><ul style="" class="pre-numbering"><li>1</li></ul>

MAX_CACHED_APPS定义如下:

<code class="hljs vhdl has-numbering">    // The maximum number <span class="hljs-keyword">of</span> cached processes we will keep around before killing them.
    // NOTE: this <span class="hljs-keyword">constant</span> <span class="hljs-keyword">is</span> *only* a control <span class="hljs-keyword">to</span> <span class="hljs-keyword">not</span> let us go too crazy <span class="hljs-keyword">with</span>
    // keeping around processes <span class="hljs-keyword">on</span> devices <span class="hljs-keyword">with</span> large amounts <span class="hljs-keyword">of</span> RAM.  <span class="hljs-keyword">For</span> devices that
    // are tighter <span class="hljs-keyword">on</span> RAM, the <span class="hljs-keyword">out</span> <span class="hljs-keyword">of</span> memory killer <span class="hljs-keyword">is</span> responsible <span class="hljs-keyword">for</span> killing background
    // processes as RAM <span class="hljs-keyword">is</span> needed, <span class="hljs-keyword">and</span> we should *never* be relying <span class="hljs-keyword">on</span> this limit <span class="hljs-keyword">to</span>
    // kill them.  Also note that this limit only applies <span class="hljs-keyword">to</span> cached background processes;
    // we have no limit <span class="hljs-keyword">on</span> the number <span class="hljs-keyword">of</span> service, visible, foreground, <span class="hljs-keyword">or</span> other such
    // processes <span class="hljs-keyword">and</span> the number <span class="hljs-keyword">of</span> those processes does <span class="hljs-keyword">not</span> count against the cached
    // <span class="hljs-keyword">process</span> limit.
    static final int MAX_CACHED_APPS = <span class="hljs-number">32</span>;</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li></ul>

我们看到了,初始值系统中的所有后台进程,也就是空进程和cache进程的总和不能超过32个。但是,用户可以设置这个值,在settings的开发者选项中可以设置,如下图:
这里写图片描述
用户可以点击上图中的“后台进程限制”来设置对应的值。
不过一般情况下,这个值都是32.现在我们就以32为值来分析一下代码,我们看到上面的代码中最终会走到最后一个分支,这个分支中针对emptyProcessLimit和cachedProcessLimit分别赋值,emptyProcessLimit的值是通过ProcessList的computeEmptyProcessLimit获取的:
computeEmptyProcessLimit@ProcessList.java

<code class="hljs cs has-numbering">    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">computeEmptyProcessLimit</span>(<span class="hljs-keyword">int</span> totalProcessLimit) {
        <span class="hljs-keyword">return</span> totalProcessLimit/<span class="hljs-number">2</span>;
    }</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li></ul>

我们看到这里的操作很简单,直接把mProcessLimit除以2,然后返回。这里除以2的意思就是空进程和cache进程数目对半分,各为16个。
我们继续看接下来的代码:

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

        mNumNonCachedProcs = <span class="hljs-number">0</span>;
        mNumCachedHiddenProcs = <span class="hljs-number">0</span>;</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li></ul>

这段代码首先计算了cache进程的slot数目,结果是(15-9+1)/2=3,然后是计算了空进程的数目,直接用mLruProcesses总数目减去mNumNonCachedProcs(非cache进程,正常进程)和mNumCachedHiddenProcs(cache进程)得到。然后就是emptyFactor(每个slot的empty的进程数)和cachedFactor(每个slot的cache进程数)的计算,方法是对应的总数目除以slot数目。这些变量有什么意义呢?下面的代码给了我们答案:

<code class="hljs cs has-numbering"><span class="hljs-comment">// First update the OOM adjustment for each of the</span>
<span class="hljs-comment">// application processes based on their current state.</span>
<span class="hljs-comment">// 获得cache adj值</span>
<span class="hljs-keyword">int</span> curCachedAdj = ProcessList.CACHED_APP_MIN_ADJ;
<span class="hljs-keyword">int</span> nextCachedAdj = curCachedAdj+<span class="hljs-number">1</span>;
<span class="hljs-keyword">int</span> curEmptyAdj = ProcessList.CACHED_APP_MIN_ADJ;
<span class="hljs-keyword">int</span> nextEmptyAdj = curEmptyAdj+<span class="hljs-number">2</span>;
<span class="hljs-comment">// 从mLruProcesses的顶部,循环操作</span>
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i=N-<span class="hljs-number">1</span>; i>=<span class="hljs-number">0</span>; i--) {
    ProcessRecord app = mLruProcesses.<span class="hljs-keyword">get</span>(i);
    <span class="hljs-comment">// 如果这个app进程没有被AMS杀死并且主线程不为空(已经运行)的话,就执行下面操作</span>
    <span class="hljs-keyword">if</span> (!app.killedByAm && app.thread != <span class="hljs-keyword">null</span>) {
        app.procStateChanged = <span class="hljs-keyword">false</span>;
        <span class="hljs-comment">// 通过computeOomAdjLocked计算app进程的oom_adj值,这是一个重要操作,下面我们详细分析</span>
        computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, <span class="hljs-keyword">true</span>, now);

        <span class="hljs-comment">// If we haven't yet assigned the final cached adj</span>
        <span class="hljs-comment">// to the process, do that now.</span>
        <span class="hljs-comment">// 如上面的注释,如果我们还没有设置oom_adj值的话,换句话说,那就是我们经过computeOomAdjLocked计算之后我们的oom_adj的值还是比UNKNOWN_ADJ要大,说明这个进程是cache进程或者空进程</span>
        <span class="hljs-keyword">if</span> (app.curAdj >= ProcessList.UNKNOWN_ADJ) {
            <span class="hljs-keyword">switch</span> (app.curProcState) {
                <span class="hljs-comment">// 以下两个case说明是cache进程</span>
                <span class="hljs-keyword">case</span> ActivityManager.PROCESS_STATE_CACHED_ACTIVITY:
                <span class="hljs-keyword">case</span> ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
                    <span class="hljs-comment">// This process is a cached process holding activities...</span>
                    <span class="hljs-comment">// assign it the next cached value for that type, and then</span>
                    <span class="hljs-comment">// step that cached level.</span>
                    app.curRawAdj = curCachedAdj;
                    <span class="hljs-comment">// 通过modifyRawOomAdj调整当前app进程的oom_adj的值</span>
                    app.curAdj = app.modifyRawOomAdj(curCachedAdj);
                    <span class="hljs-keyword">if</span> (DEBUG_LRU && <span class="hljs-keyword">false</span>) Slog.d(TAG_LRU, <span class="hljs-string">"Assigning activity LRU #"</span> + i
                            + <span class="hljs-string">" adj: "</span> + app.curAdj + <span class="hljs-string">" (curCachedAdj="</span> + curCachedAdj
                            + <span class="hljs-string">")"</span>);
                    <span class="hljs-comment">// 没有达到最大值CACHED_APP_MAX_ADJ</span>
                    <span class="hljs-keyword">if</span> (curCachedAdj != nextCachedAdj) {
                        stepCached++;
                        <span class="hljs-comment">// 如果当前slot中的cache进程数大于前面算出的cachedFactor的话就要进入下一个slot,这么做的目的就是使得从CACHED_APP_MIN_ADJ到CACHED_APP_MAX_ADJ之间的cache进程数目分配均匀,否则就会出现很多优先级相差很多的cache进程拥有相近的oom_adj的值,这样的结果是不合理的。</span>
                        <span class="hljs-keyword">if</span> (stepCached >= cachedFactor) {
                            stepCached = <span class="hljs-number">0</span>;
                            curCachedAdj = nextCachedAdj;
                            nextCachedAdj += <span class="hljs-number">2</span>;
                            <span class="hljs-keyword">if</span> (nextCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) {
                                nextCachedAdj = ProcessList.CACHED_APP_MAX_ADJ;
                            }
                        }
                    }
                    <span class="hljs-keyword">break</span>;
                <span class="hljs-comment">// 这个case就是empty进程了,处理的逻辑和上面的cache进程是一样的</span>
                <span class="hljs-keyword">default</span>:
                    <span class="hljs-comment">// For everything else, assign next empty cached process</span>
                    <span class="hljs-comment">// level and bump that up.  Note that this means that</span>
                    <span class="hljs-comment">// long-running services that have dropped down to the</span>
                    <span class="hljs-comment">// cached level will be treated as empty (since their process</span>
                    <span class="hljs-comment">// state is still as a service), which is what we want.</span>
                    app.curRawAdj = curEmptyAdj;
                    app.curAdj = app.modifyRawOomAdj(curEmptyAdj);
                    <span class="hljs-keyword">if</span> (DEBUG_LRU && <span class="hljs-keyword">false</span>) Slog.d(TAG_LRU, <span class="hljs-string">"Assigning empty LRU #"</span> + i
                            + <span class="hljs-string">" adj: "</span> + app.curAdj + <span class="hljs-string">" (curEmptyAdj="</span> + curEmptyAdj
                            + <span class="hljs-string">")"</span>);
                    <span class="hljs-keyword">if</span> (curEmptyAdj != nextEmptyAdj) {
                        stepEmpty++;
                        <span class="hljs-keyword">if</span> (stepEmpty >= emptyFactor) {
                            stepEmpty = <span class="hljs-number">0</span>;
                            curEmptyAdj = nextEmptyAdj;
                            nextEmptyAdj += <span class="hljs-number">2</span>;
                            <span class="hljs-keyword">if</span> (nextEmptyAdj > ProcessList.CACHED_APP_MAX_ADJ) {
                                nextEmptyAdj = ProcessList.CACHED_APP_MAX_ADJ;
                            }
                        }
                    }
                    <span class="hljs-keyword">break</span>;
            }
        }

        <span class="hljs-comment">// 应用更新oom_adj后的数据,主要就是更新进程的各种oom_adj的值</span>
        applyOomAdjLocked(app, <span class="hljs-keyword">true</span>, now);

        <span class="hljs-comment">// Count the number of process types.</span>
        <span class="hljs-comment">// 统计各种类型进程的数目</span>
        <span class="hljs-keyword">switch</span> (app.curProcState) {
            <span class="hljs-keyword">case</span> ActivityManager.PROCESS_STATE_CACHED_ACTIVITY:
            <span class="hljs-keyword">case</span> ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
                mNumCachedHiddenProcs++;
                numCached++;
                <span class="hljs-keyword">if</span> (numCached > cachedProcessLimit) {
                    app.kill(<span class="hljs-string">"cached #"</span> + numCached, <span class="hljs-keyword">true</span>);
                }
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> ActivityManager.PROCESS_STATE_CACHED_EMPTY:
                <span class="hljs-keyword">if</span> (numEmpty > ProcessList.TRIM_EMPTY_APPS
                        && app.lastActivityTime < oldTime) {
                    app.kill(<span class="hljs-string">"empty for "</span>
                            + ((oldTime + ProcessList.MAX_EMPTY_TIME - app.lastActivityTime)
                            / <span class="hljs-number">1000</span>) + <span class="hljs-string">"s"</span>, <span class="hljs-keyword">true</span>);
                } <span class="hljs-keyword">else</span> {
                    numEmpty++;
                    <span class="hljs-keyword">if</span> (numEmpty > emptyProcessLimit) {
                        app.kill(<span class="hljs-string">"empty #"</span> + numEmpty, <span class="hljs-keyword">true</span>);
                    }
                }
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">default</span>:
                mNumNonCachedProcs++;
                <span class="hljs-keyword">break</span>;
        }

        <span class="hljs-comment">// 下面的注释写的很清楚,意思就是如果这个app进程是独立的,并且这个app中没有任何的service,那么这个进程就没有存在的必要的,直接杀死。</span>
        <span class="hljs-keyword">if</span> (app.isolated && app.services.size() <= <span class="hljs-number">0</span>) {
            <span class="hljs-comment">// If this is an isolated process, and there are no</span>
            <span class="hljs-comment">// services running in it, then the process is no longer</span>
            <span class="hljs-comment">// needed.  We agressively kill these because we can by</span>
            <span class="hljs-comment">// definition not re-use the same process again, and it is</span>
            <span class="hljs-comment">// good to avoid having whatever code was running in them</span>
            <span class="hljs-comment">// left sitting around after no longer needed.</span>
            app.kill(<span class="hljs-string">"isolated not needed"</span>, <span class="hljs-keyword">true</span>);
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-comment">// Keeping this process, update its uid.</span>
            <span class="hljs-comment">// 否则就保留这个进程</span>
            final UidRecord uidRec = app.uidRecord;
            <span class="hljs-keyword">if</span> (uidRec != <span class="hljs-keyword">null</span> && uidRec.curProcState > app.curProcState) {
                uidRec.curProcState = app.curProcState;
            }
        }

        <span class="hljs-comment">// 如果这个app进程的重要级别比home的级别低的话,那么就增加trim计数,这个计数在后面清除多余app进程时会用到。</span>
        <span class="hljs-keyword">if</span> (app.curProcState >= ActivityManager.PROCESS_STATE_HOME
                && !app.killedByAm) {
            numTrimming++;
        }
    }
}</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li><li>53</li><li>54</li><li>55</li><li>56</li><li>57</li><li>58</li><li>59</li><li>60</li><li>61</li><li>62</li><li>63</li><li>64</li><li>65</li><li>66</li><li>67</li><li>68</li><li>69</li><li>70</li><li>71</li><li>72</li><li>73</li><li>74</li><li>75</li><li>76</li><li>77</li><li>78</li><li>79</li><li>80</li><li>81</li><li>82</li><li>83</li><li>84</li><li>85</li><li>86</li><li>87</li><li>88</li><li>89</li><li>90</li><li>91</li><li>92</li><li>93</li><li>94</li><li>95</li><li>96</li><li>97</li><li>98</li><li>99</li><li>100</li><li>101</li><li>102</li><li>103</li><li>104</li><li>105</li><li>106</li><li>107</li><li>108</li><li>109</li><li>110</li><li>111</li><li>112</li><li>113</li><li>114</li><li>115</li><li>116</li><li>117</li><li>118</li><li>119</li><li>120</li><li>121</li><li>122</li><li>123</li><li>124</li><li>125</li><li>126</li><li>127</li><li>128</li><li>129</li><li>130</li><li>131</li></ul>

上面代码中我的注释将这段程序的基本逻辑描述了,这里就不再赘述。这里需要额外说明一下,我们上面通过调用modifyRawOomAdj方法调整对应app进程的oom_adj值,我们下面看一下是怎么调整的:
modifyRawOomAdj@ProcessRecord.java

<code class="hljs cs has-numbering">    <span class="hljs-keyword">int</span> modifyRawOomAdj(<span class="hljs-keyword">int</span> adj) {
        <span class="hljs-keyword">if</span> (hasAboveClient) {
            <span class="hljs-comment">// If this process has bound to any services with BIND_ABOVE_CLIENT,</span>
            <span class="hljs-comment">// then we need to drop its adjustment to be lower than the service's</span>
            <span class="hljs-comment">// in order to honor the request.  We want to drop it by one adjustment</span>
            <span class="hljs-comment">// level...  but there is special meaning applied to various levels so</span>
            <span class="hljs-comment">// we will skip some of them.</span>
            <span class="hljs-keyword">if</span> (adj < ProcessList.FOREGROUND_APP_ADJ) {
                <span class="hljs-comment">// System process will not get dropped, ever</span>
            } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (adj < ProcessList.VISIBLE_APP_ADJ) {
                adj = ProcessList.VISIBLE_APP_ADJ;
            } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (adj < ProcessList.PERCEPTIBLE_APP_ADJ) {
                adj = ProcessList.PERCEPTIBLE_APP_ADJ;
            } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (adj < ProcessList.CACHED_APP_MIN_ADJ) {
                adj = ProcessList.CACHED_APP_MIN_ADJ;
            } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (adj < ProcessList.CACHED_APP_MAX_ADJ) {
                adj++;
            }
        }
        <span class="hljs-keyword">return</span> adj;
    }</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li></ul>

这里的操作分两种情况:如果hasAboveClient为true和false。hasAboveClient定义如下:

<code class="hljs java has-numbering"><span class="hljs-keyword">boolean</span> hasAboveClient;     <span class="hljs-comment">// Bound using BIND_ABOVE_CLIENT, so want to be lower</span></code><ul style="" class="pre-numbering"><li>1</li></ul>

这里的注释和上面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:

<code class="hljs coffeescript has-numbering"><span class="hljs-keyword">if</span> (app == TOP_APP) {
    <span class="hljs-regexp">//</span> The last app <span class="hljs-literal">on</span> the list <span class="hljs-keyword">is</span> the foreground app.
    adj = ProcessList.FOREGROUND_APP_ADJ;
    schedGroup = Process.THREAD_GROUP_DEFAULT;
    app.adjType = <span class="hljs-string">"top-activity"</span>;
    foregroundActivities = <span class="hljs-literal">true</span>;
    procState = PROCESS_STATE_TOP;
}</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li></ul>

2). 如果进程有instrumentation实例,说明这个app正在测试当中,这个时候它的adj应该是一个前台adj,保证尽量不被杀死:

<code class="hljs cs has-numbering"><span class="hljs-keyword">if</span> (app.instrumentationClass != <span class="hljs-keyword">null</span>) {
    <span class="hljs-comment">// Don't want to kill running instrumentation.</span>
    adj = ProcessList.FOREGROUND_APP_ADJ;
    schedGroup = Process.THREAD_GROUP_DEFAULT;
    app.adjType = <span class="hljs-string">"instrumentation"</span>;
    procState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
}</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li></ul>

3). 如果进程注册了广播接受器,则把它的oom_adj设置为前台adj:

<code class="hljs mizar has-numbering">if ((queue = isReceivingBroadcast(app)) != null) {
    // An app <span class="hljs-keyword">that</span> <span class="hljs-keyword">is</span> currently receiving a broadcast also
    // counts <span class="hljs-keyword">as</span> <span class="hljs-keyword">being</span> <span class="hljs-keyword">in</span> the foreground <span class="hljs-keyword">for</span> OOM killer purposes.
    // It's placed <span class="hljs-keyword">in</span> a sched group based on the nature <span class="hljs-keyword">of</span> the
    // broadcast <span class="hljs-keyword">as</span> reflected <span class="hljs-keyword">by</span> which queue it's active <span class="hljs-keyword">in</span>.
    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;
}</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li></ul>

4). 如果进程中有正在运行的service,则把它的oom_adj设置为前台adj:

<code class="hljs avrasm has-numbering">if (app<span class="hljs-preprocessor">.executingServices</span><span class="hljs-preprocessor">.size</span>() > <span class="hljs-number">0</span>) {
    // An app that is currently executing a service callback also
    // counts as being <span class="hljs-keyword">in</span> the foreground.
    adj = ProcessList<span class="hljs-preprocessor">.FOREGROUND</span>_APP_ADJ<span class="hljs-comment">;</span>
    schedGroup = app<span class="hljs-preprocessor">.execServicesFg</span> ?
            Process<span class="hljs-preprocessor">.THREAD</span>_GROUP_DEFAULT : Process<span class="hljs-preprocessor">.THREAD</span>_GROUP_BG_NONINTERACTIVE<span class="hljs-comment">;</span>
    app<span class="hljs-preprocessor">.adjType</span> = <span class="hljs-string">"exec-service"</span><span class="hljs-comment">;</span>
    procState = ActivityManager<span class="hljs-preprocessor">.PROCESS</span>_STATE_SERVICE<span class="hljs-comment">;</span>
    //Slog<span class="hljs-preprocessor">.i</span>(TAG, <span class="hljs-string">"EXEC "</span> + (app<span class="hljs-preprocessor">.execServicesFg</span> ? <span class="hljs-string">"FG"</span> : <span class="hljs-string">"BG"</span>) + <span class="hljs-string">": "</span> + app)<span class="hljs-comment">;</span>
}</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li></ul>

5). 其他的进程就先设置oom_adj为cachedAdj,在updateOomAdjLocked中我们传递的参数是UNKNOWN_ADJ。
上面的几个步骤只是计算了进程了oom_adj的一个初始值,接下来的代码还有针对这个值继续调整,但是调整的原则就是向下调整,只有判断出进程的oom_adj的值可以更低的时候才会去改变它。
6). 如果进程有可见的activity的话,那么将它的adj设置为VISIBLE_APP_ADJ:

<code class="hljs php has-numbering"><span class="hljs-keyword">if</span> (r.visible) {
    <span class="hljs-comment">// App has a visible activity; only upgrade adjustment.</span>
    <span class="hljs-keyword">if</span> (adj > ProcessList.VISIBLE_APP_ADJ) {
        adj = ProcessList.VISIBLE_APP_ADJ;
        app.adjType = <span class="hljs-string">"visible"</span>;
    }
    <span class="hljs-keyword">if</span> (procState > PROCESS_STATE_TOP) {
        procState = PROCESS_STATE_TOP;
    }
    schedGroup = Process.THREAD_GROUP_DEFAULT;
    app.cached = <span class="hljs-keyword">false</span>;
    app.<span class="hljs-keyword">empty</span> = <span class="hljs-keyword">false</span>;
    foregroundActivities = <span class="hljs-keyword">true</span>;
    <span class="hljs-keyword">break</span>;
}</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li></ul>

7). 如果进程的activity处于暂停或者正在暂停的情况下,adj调整为PERCEPTIBLE_APP_ADJ:

<code class="hljs avrasm has-numbering">if (r<span class="hljs-preprocessor">.state</span> == ActivityState<span class="hljs-preprocessor">.PAUSING</span> || r<span class="hljs-preprocessor">.state</span> == ActivityState<span class="hljs-preprocessor">.PAUSED</span>) {
    if (adj > ProcessList<span class="hljs-preprocessor">.PERCEPTIBLE</span>_APP_ADJ) {
        adj = ProcessList<span class="hljs-preprocessor">.PERCEPTIBLE</span>_APP_ADJ<span class="hljs-comment">;</span>
        app<span class="hljs-preprocessor">.adjType</span> = <span class="hljs-string">"pausing"</span><span class="hljs-comment">;</span>
    }
    if (procState > PROCESS_STATE_TOP) {
        procState = PROCESS_STATE_TOP<span class="hljs-comment">;</span>
    }
    schedGroup = Process<span class="hljs-preprocessor">.THREAD</span>_GROUP_DEFAULT<span class="hljs-comment">;</span>
    app<span class="hljs-preprocessor">.cached</span> = false<span class="hljs-comment">;</span>
    app<span class="hljs-preprocessor">.empty</span> = false<span class="hljs-comment">;</span>
    foregroundActivities = true<span class="hljs-comment">;</span>
}</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li></ul>

8). 如果进程的activity正在处于停止状态的话,那么adj设置为PERCEPTIBLE_APP_ADJ:

<code class="hljs coffeescript has-numbering"><span class="hljs-keyword">if</span> (r.state == ActivityState.STOPPING) {
    <span class="hljs-keyword">if</span> (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
        adj = ProcessList.PERCEPTIBLE_APP_ADJ;
        app.adjType = <span class="hljs-string">"stopping"</span>;
    }
    <span class="hljs-regexp">//</span> For the process state, we will at <span class="hljs-keyword">this</span> point consider the
    <span class="hljs-regexp">//</span> process to be cached.  It will be cached either as an activity
    <span class="hljs-regexp">//</span> <span class="hljs-keyword">or</span> empty depending <span class="hljs-literal">on</span> whether the activity <span class="hljs-keyword">is</span> finishing.  We <span class="hljs-keyword">do</span>
    <span class="hljs-regexp">//</span> <span class="hljs-keyword">this</span> so that we can treat the process as cached <span class="hljs-keyword">for</span> purposes <span class="hljs-keyword">of</span>
    <span class="hljs-regexp">//</span> memory trimming (determing current memory level, trim command to
    <span class="hljs-regexp">//</span> send to process) since there can be an arbitrary number <span class="hljs-keyword">of</span> stopping
    <span class="hljs-regexp">//</span> processes <span class="hljs-keyword">and</span> they should soon all go into the cached state.
    <span class="hljs-keyword">if</span> (!r.finishing) {
        <span class="hljs-keyword">if</span> (procState > ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
            procState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
        }
    }
    app.cached = <span class="hljs-literal">false</span>;
    app.empty = <span class="hljs-literal">false</span>;
    foregroundActivities = <span class="hljs-literal">true</span>;
}</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li></ul>

9). 如果adj大于了PERCEPTIBLE_APP_ADJ但是进程中有设置为前台运行的service或者app被设置为前台运行,那么adj设置为PERCEPTIBLE_APP_ADJ:

<code class="hljs avrasm has-numbering">if (adj > ProcessList<span class="hljs-preprocessor">.PERCEPTIBLE</span>_APP_ADJ) {
  if (app<span class="hljs-preprocessor">.foregroundServices</span>) {
      // The user is aware of this app, so make it visible.
      adj = ProcessList<span class="hljs-preprocessor">.PERCEPTIBLE</span>_APP_ADJ<span class="hljs-comment">;</span>
      procState = ActivityManager<span class="hljs-preprocessor">.PROCESS</span>_STATE_FOREGROUND_SERVICE<span class="hljs-comment">;</span>
      app<span class="hljs-preprocessor">.cached</span> = false<span class="hljs-comment">;</span>
      app<span class="hljs-preprocessor">.adjType</span> = <span class="hljs-string">"fg-service"</span><span class="hljs-comment">;</span>
      schedGroup = Process<span class="hljs-preprocessor">.THREAD</span>_GROUP_DEFAULT<span class="hljs-comment">;</span>
  } else if (app<span class="hljs-preprocessor">.forcingToForeground</span> != null) {
      // The user is aware of this app, so make it visible.
      adj = ProcessList<span class="hljs-preprocessor">.PERCEPTIBLE</span>_APP_ADJ<span class="hljs-comment">;</span>
      procState = ActivityManager<span class="hljs-preprocessor">.PROCESS</span>_STATE_IMPORTANT_FOREGROUND<span class="hljs-comment">;</span>
      app<span class="hljs-preprocessor">.cached</span> = false<span class="hljs-comment">;</span>
      app<span class="hljs-preprocessor">.adjType</span> = <span class="hljs-string">"force-fg"</span><span class="hljs-comment">;</span>
      app<span class="hljs-preprocessor">.adjSource</span> = app<span class="hljs-preprocessor">.forcingToForeground</span><span class="hljs-comment">;</span>
      schedGroup = Process<span class="hljs-preprocessor">.THREAD</span>_GROUP_DEFAULT<span class="hljs-comment">;</span>
  }
}</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li></ul>

10). 如果进程是heavy weight进程的话,将它的adj调整为:HEAVY_WEIGHT_APP_ADJ:

<code class="hljs cs has-numbering"><span class="hljs-keyword">if</span> (app == mHeavyWeightProcess) {
    <span class="hljs-keyword">if</span> (adj > ProcessList.HEAVY_WEIGHT_APP_ADJ) {
        <span class="hljs-comment">// We don't want to kill the current heavy-weight process.</span>
        adj = ProcessList.HEAVY_WEIGHT_APP_ADJ;
        schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
        app.cached = <span class="hljs-keyword">false</span>;
        app.adjType = <span class="hljs-string">"heavy"</span>;
    }
    <span class="hljs-keyword">if</span> (procState > ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) {
        procState = ActivityManager.PROCESS_STATE_HEAVY_WEIGHT;
    }
}</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li></ul>

这里所谓的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:

<code class="hljs cs has-numbering"><span class="hljs-keyword">if</span> (app == mHomeProcess) {
    <span class="hljs-keyword">if</span> (adj > ProcessList.HOME_APP_ADJ) {
        <span class="hljs-comment">// This process is hosting what we currently consider to be the</span>
        <span class="hljs-comment">// home app, so we don't want to let it go into the background.</span>
        adj = ProcessList.HOME_APP_ADJ;
        schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
        app.cached = <span class="hljs-keyword">false</span>;
        app.adjType = <span class="hljs-string">"home"</span>;
    }
    <span class="hljs-keyword">if</span> (procState > ActivityManager.PROCESS_STATE_HOME) {
        procState = ActivityManager.PROCESS_STATE_HOME;
    }
}</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li></ul>

12). 如果这个进程是Previous进程,并且它有activity的话,那么我们将它的adj设置为PREVIOUS_APP_ADJ:

<code class="hljs avrasm has-numbering">if (app == mPreviousProcess && app<span class="hljs-preprocessor">.activities</span><span class="hljs-preprocessor">.size</span>() > <span class="hljs-number">0</span>) {
    if (adj > ProcessList<span class="hljs-preprocessor">.PREVIOUS</span>_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<span class="hljs-preprocessor">.PREVIOUS</span>_APP_ADJ<span class="hljs-comment">;</span>
        schedGroup = Process<span class="hljs-preprocessor">.THREAD</span>_GROUP_BG_NONINTERACTIVE<span class="hljs-comment">;</span>
        app<span class="hljs-preprocessor">.cached</span> = false<span class="hljs-comment">;</span>
        app<span class="hljs-preprocessor">.adjType</span> = <span class="hljs-string">"previous"</span><span class="hljs-comment">;</span>
    }
    if (procState > ActivityManager<span class="hljs-preprocessor">.PROCESS</span>_STATE_LAST_ACTIVITY) {
        procState = ActivityManager<span class="hljs-preprocessor">.PROCESS</span>_STATE_LAST_ACTIVITY<span class="hljs-comment">;</span>
    }
}</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li></ul>

所谓的previous进程就是当前activity的前一个activity所在的进程,这个进程需要比较高的优先级,因为用户很有可能会再次回到这个进程中。
13). 如果进程正在备份的话,那么将它的adj设置为BACKUP_APP_ADJ:

<code class="hljs cs has-numbering"><span class="hljs-keyword">if</span> (mBackupTarget != <span class="hljs-keyword">null</span> && app == mBackupTarget.app) {
    <span class="hljs-comment">// If possible we want to avoid killing apps while they're being backed up</span>
    <span class="hljs-keyword">if</span> (adj > ProcessList.BACKUP_APP_ADJ) {
        <span class="hljs-keyword">if</span> (DEBUG_BACKUP) Slog.v(TAG_BACKUP, <span class="hljs-string">"oom BACKUP_APP_ADJ for "</span> + app);
        adj = ProcessList.BACKUP_APP_ADJ;
        <span class="hljs-keyword">if</span> (procState > ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND) {
            procState = ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND;
        }
        app.adjType = <span class="hljs-string">"backup"</span>;
        app.cached = <span class="hljs-keyword">false</span>;
    }
    <span class="hljs-keyword">if</span> (procState > ActivityManager.PROCESS_STATE_BACKUP) {
        procState = ActivityManager.PROCESS_STATE_BACKUP;
    }
}</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li></ul>

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。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值