天天看點

Android 程序管理

在Android中,程序(process)的概念被弱化,傳統的程序是程式執行的載體,程序退出意味着應用的關閉。但是在Android中程序知識一個運作元件的容器,當系統需要運作一個元件時,啟動包含它的程序,當元件不在使用時,程序也會被關閉。例如一個APK檔案中的兩個service,可以運作在一個程序中,也可以運作在各自的程序中。

雖然在Android的應用開發中,不再強調程序的概念,但是在AMS中,還必須管理和排程程序,AMS對程序的管理,主要展現在兩方面:一是動态的調整程序在mLruProcesss清單的位置,二是調整程序oom_adj的值,這兩項調整和系統進行自動記憶體回收有關。當記憶體不足時,系統會關閉一些程序來釋放記憶體。

系統主要根據程序的oom_adj值來挑選要殺死的程序,oom_adj值越大表示程序越可能被殺死。

1. 啟動程序

AMS中啟動一個程序調用的是addAppLocked()方法,代碼如下:

final ProcessResord addAppLocked(ApplicationInfo info, boolean isolated){

  ProcessRecord app;

   //isolated為true表示要啟動一個新程序

  if(!isolated){ 

     app=getProcessResordLocked(info.processName,info.uid,true);                                                                                       

   }else{

     app=null;

   }

   if(app=null){

       //建立一個ProcessRecord對象

       app=newProcessRecordLocked(info,null,0);

       mProcessNames.put(info.processName,app.uid,app);

       if(isolated){

           mIsolatedProcesses.put(app.uid,app);

       }

       updateLruProcessLocked(app,false,null);

       updateOomAdjLocked();

   }

   .....

  if(app.thred==null&&mPersistentStartingProcesses.indexOf(app)<0){   

       mPersistentStartingProcesses.add(app);

       //啟動程序

       startProcessLocked(app,"added application",app.processName   

        abiOverride,null ,null );

  }

  return app;

}

addAppLocked()方法會根據參數參數isolated來決定是否啟動一個新程序,如果isoated為true,即使系統中可能已經有一個同名的程序存在,也會再建立一個新錦成。getProcessRecordLocked()方法用來目前運作的程序清單查找程序。newProcessRecordLocked()方法用來建立一個ProcessRecord的資料結構。updateLruProcessLocked方法用來更新運作程序的狀态,updateOomAdjLocked()方法用來更新程序的優先級,這兩個方法是Process的管理核心。

首先看看startProcessLocked()是啟動程序的方法,看看它的代碼:

private final void startProcessLocked(ProcessRecord app,String hostingType,String abiOverride){

    if(app.pid>0&&app.pid!=MY_PID){

         synchronized(mpidSelfLocked){

         mPidSelfLocked.remove(app.pid);//把程序id先移除,防止重複

         //把消息PROC_START_TIMEOUT_MSG也清除

         mHandler.removeMessages(PROC_START_TIMEOUT_MSG,app);

         }

         app.setpid(0);

    }

    mProcessOnHold.remove(app);

    ......

    try{

        final int uid=app.uid;

        int[] gids=null;

        intmountExternal=zygote.MOUNT_EXTERNAL_NONE;

        if(!app.isolate){

            int [] permGids=null;

            try{

                 final PackageManager   

                       pm=mContext.getPackagesManager();

                 //檢查程序權限,确定它是否能看見所有使用者的存儲空間

                 if(Enviroment.isExternalStorageEmulated()){

                    if(pm.checkPermission(.....)){

             mountExternal=Zygote.MOUNTEXTERNAL_MULTIUSER_ALL;

                    else{

                      mountExternal=Zygote.MOUNTEXTERNAL_MULTIUSER;

                    }          

                    }

                 }catch (PackageManager.NameNotFountException e){.}

            }

            ......

            //啟動應用

            Process.ProcessStartResult startResult=Process.start(

                app.processName,uid,uid,gids,debugFlags,

                mountExternal,app.info.targetSdkVersion,

                app.info.seinfo,requiredAbi,instructionSet,

                app.infodataDir,entryPointArgs)

                .....

            synchornized(mPidSelfLocked){

               //發送一個定時消息,時間到應用還沒啟動完成就會出現ANR

               this.mPidSelfLocked.put(startResult.pid,app);

               if(isActivityProcess){

                  Message msg=mHandler.obtainMessage(

                  PROC_START_TIME_OUT_MSG);

                  msg.obj=app;

                  mHandler.sendMessageDelayed(msg,startResult

                  .usingWrapper?PROC_START_WITH_WRAPPER:

                  PROC_STRAT_TIMEOUT);

               }

            }  

        }catch(RuntimeException e){

            app.setpid(0);

            mBatteryStatsService.noteProcessFinish(

            app.processName,app.info.uid);

            if(app.isolated){

                mBatteryStatsService.removeIsolatedUid(

                app.uid,app.info.uid);

            }

        }

    }

}

startProcessLocked()方法的流程是,準備好啟動應用的參數,調用Process類的start來啟動程序,啟動程序後AMS給自己發送了一個PROC_START_TIMEOUT_MSG的消息,來防止程序啟動逾時。如果start()函數傳回的結果中usingWrapper的值為true,逾時時間設為1200秒。

static final int PROC_START_TIMEOUT_WITH_WRAPPER=1200*1000;

否則逾時時間設為10秒。

static final int PROC_START_TIMEOUT=10*1000;

如果時間到了,但是程序還沒啟動完成,AMS将彈出發生ANR的對話框。

2. 調整程序的位置

AMS的代碼中經常調用updateLruProcessLocked()方法來調整某個程序在mLruProcesses清單的位置,mLruProcess是最近使用程序清單(List Of Recent Using的縮寫)。每當程序的Activity或者Service發生變化時,意味着程序活動發生了活動,是以,調用這個方法将調整到盡可能最高的位置,同時還要更新關聯程序的位置。在mLruProcesses清單中,最近活動的程序總是位于最高位置。同時擁有Activity的程序的位置總是高于隻有Service的程序位置。

AMS的成員變量mLruProcessActivityStart和mLruProcessServiceStart分别指向位置最高的、帶有Activity程序和沒有Activity程序。

updateLruProcessLocked()方法的代碼如下:

final void updateLruProcessLocked(ProcessRecord app,boolean activityChange, ProcessRecord client){

    //app.activities.size()大于0,表示本程序有活動的Activity。

    //app.hasClientActivities的值為true,表示綁定了本程序的Service的

    //客戶程序有活動的Activity

    //treatLikeActivity表示Service啟動時帶有标記BIND_TREAT_LIKE_ACTVITY

    final boolean hasActivity=app.activities.size()>0

    ||app.hasClientActivities||treatLikeActivity;

    final boolean hasService=false;

    if(!activityChange&&hasActivity){

        //如果ProcessRecord對象已經有了Activity

        //再調用本方法,除非是Activity發生變化了才要

        return;

    }

    mLruSeq++;

    final long now=SystenClock.uptimeMillis();

    app.lastActivityTime=now;//更新lastActivityTime中的時間

    if(hasActivity){

       //如果程序已經初始化,而且在mLruProcesses清單位置也是最後一項

       //這樣也沒什麼可做的,退出

       final int N=mLruProcesses.size();

       if(N>0&&mLruProcesses.get(N-1)==app){

           return ;

       } 

    }else{

        //如果程序中沒有Activity,而且應景位于mLruProcesses清單的合适位置,退出

        if(mLruProcessServiceStart>0&&mLruProcesses.get(mLruProcessServiceStart-1)==app){

            return ;

        }

   }

   int lrui=mLruProcess.lastIndexOf(app);

   if(app.persistent&&lrui>=0){

       return;//帶有persistent标志的程序不需要調整,退出

   }

   if(lrui>=0){

      //如果程序已經存在,先從mLruProceses清單中移除,同時調整mLruProcessActivityStart和mLruProcessServiceStart指向的位置

      if(lrui<mLruProcessActivityStart){

         mLruProcessActivityStart--;

      }

      if(lrui<mLruProcessServiceStart){

          mLruProcessServiceStart--;

      }

      mLruProcesses.remove(lrui);

   }

   int nextIndex;

   if(hasActivity){

       final int N=mLruProcess.size();

       if(app.activities.size()==0&&mLruProcessActivityStart<N-1)){

       //程序中沒有Activity,但是它的Service客戶程序中有Activity

       mLruProcesses.add(N-1,app);//将程序插入到最後一項

       final int uid=app.info.uid;

       //如果從倒數第三項開始連續有程序的uid和插入的程序uid相同,把他們的

        位置向上移動

       for(int i=N-2;i>mLruProcessActivityStart;i--){

         processRecord subProc =mPruProcess.get(i);

         if(subProc.info.uid==uid){

            if(mLruProcesses.get(i).info.uid!=uid){

            mLruProcesses.set(i,mLruProcesses.get(i-1));

            mLruProcesses.set(i-1,temp);

            i--;

            }

         }else{

            break;

         }

       }

   }  else{

          mLruProcesses.add(app);//程序有Activity,加入到最後一項

       }

      nextIndex=mLruProcessesServiceStart;//關聯程序将要插入的位置 

   }else if(hasService){

      .....//hasService總是為False,這段不會執行

   }else{

      //如果程序中隻有Service,将程序插入到mLruProcessServiceStart指向的位置

      int index=mLruProcessServiceStart;

      if(client!=null){

          ......//基本上為null

      }

      mLruProcess.add(index,app);

      nextIndex=index-1;  //關聯程序插入的位置

      mLruProcessActivityStart++;

      mLruProcessServiceStart++;

   }

   //将和本程序的Service關聯的客戶程序的位置調整到本程序之後

   for(int j=app.connections.size()-1;j>=0;j--){

       ConnectionRecord cr=app.connections.valueAt(j);

      if(cr.binding!=null&&!crserviceDead  

        &&cr.binding.service!=null&&cr.binding.service.app

        !=null &&cr.binding.service.app.lruSeq!=mLruq

        &&!cr.binding.service.app.persistent){

         nextIndex=updateLruProcessInternalLocked(

         cr.binding.service.app,now,nextIntext,

         "service connection",cr,app);

        }

   }

  //将和本程序ContentProvider關聯的客戶程序的位置調整到本程序之後

   for(int j=app.conProviders.size()-1;j>=0;j--){

    ContentProviderRecord cpr=app.conProviders.get(j).provider;

    if(cpr.proc!=null&&cpr.proc.lruSeq!=mLruSeq

    &&!cpr.proc.persistent){

       nextIndex=updateLruProcessInternalLocked(

         cpr.proc,now,nextIntext,

         "provider reference",cpr,app);

    }

   }

}

updateLruProcessLocked()方法中調整程序很重要的一個依據是程序中有沒有活動的Activity。除了本身程序存在Activity對象之外,如果和程序中運作的Service相關聯的客戶程序有Activity,也算程序擁有Activity,這裡調整位置的目的是為了将來殺死程序釋放記憶體做準備,如果一個程序關聯程序Activity對象存在,那麼它的重要性也和真正的和擁有Activity對象的程序相當,如果殺死它,将導緻另一個程序出現嚴重錯誤,Activity用來顯示UI,關系着使用者體驗,是以盡量不關閉運作Activity元件的程序。

如果一個程序“擁有”Activity,通常會把它插到隊列的最高位置,否則,隻會把它放到所有沒有Activity程序的前面,這個位置正是變量mLruProcessServiceStart所指向的。

調整某個程序的位置之後,還要調整和該程序的關聯的位置。程序的關聯程序有兩種類型:一種是綁定了本程序服務的程序,另一種是連接配接了本程序的ContentProvider的程序。如果這些程序本身有Activity是不會調整的,需要調整的是那些沒有Activity的程序,在updateLruProcessInternalLocked()方法中會執行這種調整,但是,能調整到最高位置也就是mLruProcessServiceStart指向的位置。

updateLruProcessInternalLocked()方法代碼如下:

private final int updateLruProcessInternalLocked(ProcessRecord 

   app,long now,int index,String what,Object obj,ProcessRecord 

   srcApp){

   app.lastActivityTime=now;

   if(app.activities.size()>0){

       return index;//如果有Activity,不用調整位置。

   }

   int lrui=mLruProcesses.lastIndexOf(app);

   if(lrui<0){

      return index;//如果程序不在mLruProcesses中,退出       

   }

   if(lrui>=index){

       return index;//如果程序目前的位置高于要調整的位置,退出

   }

   if(lrui>=mLruProcessActivityStart){

      return index; //如果程序目前的位置比有Activity的程序還高,退出

   }

   //把程序調整到參數index-1的位置

   mLruProcesses.remove(this);

   if(index>0){

   index--;

   }

   mLruProcesses.add(index,app);

   return index;//傳回程序的位置

}

這裡先解釋空程序和cached程序的概念,如果一個程序不包含任何元件,該程序可以認為是空程序,例如,一個程序隻包含一個Activity,當這個Activity銷毀後變成一個”空”程序。

當Android結束一個程序時,并不會将一個程序立即從系統删除,而是把它标記為cached程序,當再次啟動新程序時,優先會使用cached程序吧,這樣會加快啟動速度。

4. 調整程序的oom_adj值

AMS中調整程序的oom_adj值的方法是updateOomAdjLocked(),代碼如下:

final void updateOomAdjLocked() {

        final ActivityRecord TOP_ACT = resumedAppLocked();

        final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null;

        final long now = SystemClock.uptimeMillis();

        final long nowElapsed = SystemClock.elapsedRealtime();

        final long oldTime = now - ProcessList.MAX_EMPTY_TIME;

        final int N = mLruProcesses.size();

        if (false) {

            RuntimeException e = new RuntimeException();

            e.fillInStackTrace();

            Slog.i(TAG, "updateOomAdj: top=" + TOP_ACT, e);

        }

        // Reset state in all uid records.

        for (int i=mActiveUids.size()-1; i>=0; i--) {

            final UidRecord uidRec = mActiveUids.valueAt(i);

            if (false && DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,

                    "Starting update of " + uidRec);

            uidRec.reset();

        }

        mStackSupervisor.rankTaskLayersIfNeeded();

        mAdjSeq++;

        mNewNumServiceProcs = 0;

        mNewNumAServiceProcs = 0;

        final int emptyProcessLimit;

        final int cachedProcessLimit;

        if (mProcessLimit <= 0) {

            emptyProcessLimit = cachedProcessLimit = 0;

        } else if (mProcessLimit == 1) {

            emptyProcessLimit = 1;

            cachedProcessLimit = 0;

        } else {

            emptyProcessLimit = ProcessList.computeEmptyProcessLimit(mProcessLimit);

            cachedProcessLimit = mProcessLimit - emptyProcessLimit;

        }

        // Let's determine how many processes we have running vs.

        // how many slots we have for background processes; we may want

        // to put multiple processes in a slot of there are enough of

        // them.

        int numSlots = (ProcessList.CACHED_APP_MAX_ADJ

                - ProcessList.CACHED_APP_MIN_ADJ + 1) / 2;

        int numEmptyProcs = N - mNumNonCachedProcs - mNumCachedHiddenProcs;

        if (numEmptyProcs > cachedProcessLimit) {

            // If there are more empty processes than our limit on cached

            // processes, then use the cached process limit for the factor.

            // This ensures that the really old empty processes get pushed

            // down to the bottom, so if we are running low on memory we will

            // have a better chance at keeping around more cached processes

            // instead of a gazillion empty processes.

            numEmptyProcs = cachedProcessLimit;

        }

        int emptyFactor = numEmptyProcs/numSlots;

        if (emptyFactor < 1) emptyFactor = 1;

        int cachedFactor = (mNumCachedHiddenProcs > 0 ? mNumCachedHiddenProcs : 1)/numSlots;

        if (cachedFactor < 1) cachedFactor = 1;

        int stepCached = 0;

        int stepEmpty = 0;

        int numCached = 0;

        int numEmpty = 0;

        int numTrimming = 0;

        mNumNonCachedProcs = 0;

        mNumCachedHiddenProcs = 0;

        // First update the OOM adjustment for each of the

        // application processes based on their current state.

        int curCachedAdj = ProcessList.CACHED_APP_MIN_ADJ;

        int nextCachedAdj = curCachedAdj+1;

        int curEmptyAdj = ProcessList.CACHED_APP_MIN_ADJ;

        int nextEmptyAdj = curEmptyAdj+2;

        ProcessRecord selectedAppRecord = null;

        long serviceLastActivity = 0;

        int numBServices = 0;

        for (int i=N-1; i>=0; i--) {

            ProcessRecord app = mLruProcesses.get(i);

            if (app == null) {

                continue;

            }

            if (mEnableBServicePropagation && app.serviceb

                    && (app.curAdj == ProcessList.SERVICE_B_ADJ)) {

                numBServices++;

                for (int s = app.services.size() - 1; s >= 0; s--) {

                    ServiceRecord sr = app.services.valueAt(s);

                    if (DEBUG_OOM_ADJ) Slog.d(TAG,"app.processName = " + app.processName

                            + " serviceb = " + app.serviceb + " s = " + s + " sr.lastActivity = "

                            + sr.lastActivity + " packageName = " + sr.packageName

                            + " processName = " + sr.processName);

                    if (SystemClock.uptimeMillis() - sr.lastActivity

                            < mMinBServiceAgingTime) {

                        if (DEBUG_OOM_ADJ) {

                            Slog.d(TAG,"Not aged enough!!!");

                        }

                        continue;

                    }

                    if (serviceLastActivity == 0 && app != TOP_APP) {

                        serviceLastActivity = sr.lastActivity;

                        selectedAppRecord = app;

                    } else if (sr.lastActivity < serviceLastActivity && app !=  TOP_APP) {

                        serviceLastActivity = sr.lastActivity;

                        selectedAppRecord = app;

                    }

                }

            }

            if (DEBUG_OOM_ADJ && selectedAppRecord != null) Slog.d(TAG,

                    "Identified app.processName = " + selectedAppRecord.processName

                    + " app.pid = " + selectedAppRecord.pid);

            if (!app.killedByAm && app.thread != null) {

                app.procStateChanged = false;

                computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now);

                // If we haven't yet assigned the final cached adj

                // to the process, do that now.

                if (app.curAdj >= ProcessList.UNKNOWN_ADJ) {

                    switch (app.curProcState) {

                        case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY:

                        case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:

                            // This process is a cached process holding activities...

                            // assign it the next cached value for that type, and then

                            // step that cached level.

                            app.curRawAdj = curCachedAdj;

                            app.curAdj = app.modifyRawOomAdj(curCachedAdj);

                            if (DEBUG_LRU && false) Slog.d(TAG_LRU, "Assigning activity LRU #" + i

                                    + " adj: " + app.curAdj + " (curCachedAdj=" + curCachedAdj

                                    + ")");

                            if (curCachedAdj != nextCachedAdj) {

                                stepCached++;

                                if (stepCached >= cachedFactor) {

                                    stepCached = 0;

                                    curCachedAdj = nextCachedAdj;

                                    nextCachedAdj += 2;

                                    if (nextCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) {

                                        nextCachedAdj = ProcessList.CACHED_APP_MAX_ADJ;

                                    }

                                }

                            }

                            break;

                        default:

                            // For everything else, assign next empty cached process

                            // level and bump that up.  Note that this means that

                            // long-running services that have dropped down to the

                            // cached level will be treated as empty (since their process

                            // state is still as a service), which is what we want.

                            app.curRawAdj = curEmptyAdj;

                            app.curAdj = app.modifyRawOomAdj(curEmptyAdj);

                            if (DEBUG_LRU && false) Slog.d(TAG_LRU, "Assigning empty LRU #" + i

                                    + " adj: " + app.curAdj + " (curEmptyAdj=" + curEmptyAdj

                                    + ")");

                            if (curEmptyAdj != nextEmptyAdj) {

                                stepEmpty++;

                                if (stepEmpty >= emptyFactor) {

                                    stepEmpty = 0;

                                    curEmptyAdj = nextEmptyAdj;

                                    nextEmptyAdj += 2;

                                    if (nextEmptyAdj > ProcessList.CACHED_APP_MAX_ADJ) {

                                        nextEmptyAdj = ProcessList.CACHED_APP_MAX_ADJ;

                                    }

                                }

                            }

                            break;

                    }

                }

                applyOomAdjLocked(app, true, now, nowElapsed);

                // Count the number of process types.

                switch (app.curProcState) {

                    case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY:

                    case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:

                        mNumCachedHiddenProcs++;

                        numCached++;

                        if (numCached > cachedProcessLimit) {

                            app.kill("cached #" + numCached, true);

                        }

                        break;

                    case ActivityManager.PROCESS_STATE_CACHED_EMPTY:

                        if (numEmpty > ProcessList.TRIM_EMPTY_APPS

                                && app.lastActivityTime < oldTime) {

                            app.kill("empty for "

                                    + ((oldTime + ProcessList.MAX_EMPTY_TIME - app.lastActivityTime)

                                    / 1000) + "s", true);

                        } else {

                            numEmpty++;

                            if (numEmpty > emptyProcessLimit) {

                                app.kill("empty #" + numEmpty, true);

                            }

                        }

                        break;

                    default:

                        mNumNonCachedProcs++;

                        break;

                }

                if (app.isolated && app.services.size() <= 0) {

                    // If this is an isolated process, and there are no

                    // services running in it, then the process is no longer

                    // needed.  We agressively kill these because we can by

                    // definition not re-use the same process again, and it is

                    // good to avoid having whatever code was running in them

                    // left sitting around after no longer needed.

                    app.kill("isolated not needed", true);

                } else {

                    // Keeping this process, update its uid.

                    final UidRecord uidRec = app.uidRecord;

                    if (uidRec != null && uidRec.curProcState > app.curProcState) {

                        uidRec.curProcState = app.curProcState;

                    }

                }

                if (app.curProcState >= ActivityManager.PROCESS_STATE_HOME

                        && !app.killedByAm) {

                    numTrimming++;

                }

            }

        }

        if ((numBServices > mBServiceAppThreshold) && (true == mAllowLowerMemLevel)

                && (selectedAppRecord != null)) {

            ProcessList.setOomAdj(selectedAppRecord.pid, selectedAppRecord.info.uid,

                    ProcessList.CACHED_APP_MAX_ADJ);

            selectedAppRecord.setAdj = selectedAppRecord.curAdj;

            if (DEBUG_OOM_ADJ) Slog.d(TAG,"app.processName = " + selectedAppRecord.processName

                        + " app.pid = " + selectedAppRecord.pid + " is moved to higher adj");

        }

        mNumServiceProcs = mNewNumServiceProcs;

        // Now determine the memory trimming level of background processes.

        // Unfortunately we need to start at the back of the list to do this

        // properly.  We only do this if the number of background apps we

        // are managing to keep around is less than half the maximum we desire;

        // if we are keeping a good number around, we'll let them use whatever

        // memory they want.

        final int numCachedAndEmpty = numCached + numEmpty;

        int memFactor;

        if (numCached <= ProcessList.TRIM_CACHED_APPS

                && numEmpty <= ProcessList.TRIM_EMPTY_APPS) {

            if (numCachedAndEmpty <= ProcessList.TRIM_CRITICAL_THRESHOLD) {

                memFactor = ProcessStats.ADJ_MEM_FACTOR_CRITICAL;

            } else if (numCachedAndEmpty <= ProcessList.TRIM_LOW_THRESHOLD) {

                memFactor = ProcessStats.ADJ_MEM_FACTOR_LOW;

            } else {

                memFactor = ProcessStats.ADJ_MEM_FACTOR_MODERATE;

            }

        } else {

            memFactor = ProcessStats.ADJ_MEM_FACTOR_NORMAL;

        }

        // We always allow the memory level to go up (better).  We only allow it to go

        // down if we are in a state where that is allowed, *and* the total number of processes

        // has gone down since last time.

        if (DEBUG_OOM_ADJ) Slog.d(TAG_OOM_ADJ, "oom: memFactor=" + memFactor

                + " last=" + mLastMemoryLevel + " allowLow=" + mAllowLowerMemLevel

                + " numProcs=" + mLruProcesses.size() + " last=" + mLastNumProcesses);

        if (memFactor > mLastMemoryLevel) {

            if (!mAllowLowerMemLevel || mLruProcesses.size() >= mLastNumProcesses) {

                memFactor = mLastMemoryLevel;

                if (DEBUG_OOM_ADJ) Slog.d(TAG_OOM_ADJ, "Keeping last mem factor!");

            }

        }

        if (memFactor != mLastMemoryLevel) {

            EventLogTags.writeAmMemFactor(memFactor, mLastMemoryLevel);

        }

        mLastMemoryLevel = memFactor;

        mLastNumProcesses = mLruProcesses.size();

        boolean allChanged = mProcessStats.setMemFactorLocked(memFactor, !isSleepingLocked(), now);

        final int trackerMemFactor = mProcessStats.getMemFactorLocked();

        if (memFactor != ProcessStats.ADJ_MEM_FACTOR_NORMAL) {

            if (mLowRamStartTime == 0) {

                mLowRamStartTime = now;

            }

            int step = 0;

            int fgTrimLevel;

            switch (memFactor) {

                case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:

                    fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL;

                    break;

                case ProcessStats.ADJ_MEM_FACTOR_LOW:

                    fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW;

                    break;

                default:

                    fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE;

                    break;

            }

            int factor = numTrimming/3;

            int minFactor = 2;

            if (mHomeProcess != null) minFactor++;

            if (mPreviousProcess != null) minFactor++;

            if (factor < minFactor) factor = minFactor;

            int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;

            for (int i=N-1; i>=0; i--) {

                ProcessRecord app = mLruProcesses.get(i);

                if(ZSFeature.ZEUSIS_FEATURE_APP_FREEZE_BACKGROUND){

                    if(app.frozenRemoved){

                        Slog.d(TAG, app.info.packageName + " is frozen and no need to scheduleTrimMemory[updateOomAdjLocked memFactor != ProcessStats.ADJ_MEM_FACTOR_NORMAL]");

                        continue;

                    }

                }

                if (allChanged || app.procStateChanged) {

                    setProcessTrackerStateLocked(app, trackerMemFactor, now);

                    app.procStateChanged = false;

                }

                if (app.curProcState >= ActivityManager.PROCESS_STATE_HOME

                        && !app.killedByAm) {

                    if (app.trimMemoryLevel < curLevel && app.thread != null) {

                        try {

                            if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,

                                    "Trimming memory of " + app.processName + " to " + curLevel);

                            app.thread.scheduleTrimMemory(curLevel);

                        } catch (RemoteException e) {

                        }

                        if (false) {

                            // For now we won't do this; our memory trimming seems

                            // to be good enough at this point that destroying

                            // activities causes more harm than good.

                            if (curLevel >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE

                                    && app != mHomeProcess && app != mPreviousProcess) {

                                // Need to do this on its own message because the stack may not

                                // be in a consistent state at this point.

                                // For these apps we will also finish their activities

                                // to help them free memory.

                                mStackSupervisor.scheduleDestroyAllActivities(app, "trim");

                            }

                        }

                    }

                    app.trimMemoryLevel = curLevel;

                    step++;

                    if (step >= factor) {

                        step = 0;

                        switch (curLevel) {

                            case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:

                                curLevel = ComponentCallbacks2.TRIM_MEMORY_MODERATE;

                                break;

                            case ComponentCallbacks2.TRIM_MEMORY_MODERATE:

                                curLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;

                                break;

                        }

                    }

                } else if (app.curProcState == ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) {

                    if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_BACKGROUND

                            && app.thread != null) {

                        try {

                            if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,

                                    "Trimming memory of heavy-weight " + app.processName

                                    + " to " + ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);

                            app.thread.scheduleTrimMemory(

                                    ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);

                        } catch (RemoteException e) {

                        }

                    }

                    app.trimMemoryLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;

                } else {

                    if ((app.curProcState >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND

                            || app.systemNoUi) && app.pendingUiClean) {

                        // If this application is now in the background and it

                        // had done UI, then give it the special trim level to

                        // have it free UI resources.

                        final int level = ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN;

                        if (app.trimMemoryLevel < level && app.thread != null) {

                            try {

                                if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,

                                        "Trimming memory of bg-ui " + app.processName

                                        + " to " + level);

                                app.thread.scheduleTrimMemory(level);

                            } catch (RemoteException e) {

                            }

                        }

                        app.pendingUiClean = false;

                    }

                    if (app.trimMemoryLevel < fgTrimLevel && app.thread != null) {

                        try {

                            if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,

                                    "Trimming memory of fg " + app.processName

                                    + " to " + fgTrimLevel);

                            app.thread.scheduleTrimMemory(fgTrimLevel);

                        } catch (RemoteException e) {

                        }

                    }

                    app.trimMemoryLevel = fgTrimLevel;

                }

            }

        } else {

            if (mLowRamStartTime != 0) {

                mLowRamTimeSinceLastIdle += now - mLowRamStartTime;

                mLowRamStartTime = 0;

            }

            for (int i=N-1; i>=0; i--) {

                ProcessRecord app = mLruProcesses.get(i);

                if(ZSFeature.ZEUSIS_FEATURE_APP_FREEZE_BACKGROUND){

                    if(app.frozenRemoved){

                        Slog.d(TAG, app.info.packageName + " is frozen and no need to scheduleTrimMemory[updateOomAdjLocked memFactor = ProcessStats.ADJ_MEM_FACTOR_NORMAL]");

                        continue;

                    }

                }

                if (allChanged || app.procStateChanged) {

                    setProcessTrackerStateLocked(app, trackerMemFactor, now);

                    app.procStateChanged = false;

                }

                if ((app.curProcState >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND

                        || app.systemNoUi) && app.pendingUiClean) {

                    if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN

                            && app.thread != null) {

                        try {

                            if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,

                                    "Trimming memory of ui hidden " + app.processName

                                    + " to " + ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);

                            app.thread.scheduleTrimMemory(

                                    ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);

                        } catch (RemoteException e) {

                        }

                    }

                    app.pendingUiClean = false;

                }

                app.trimMemoryLevel = 0;

            }

        }

        if (mAlwaysFinishActivities) {

            // Need to do this on its own message because the stack may not

            // be in a consistent state at this point.

            mStackSupervisor.scheduleDestroyAllActivities(null, "always-finish");

        }

        if (allChanged) {

            requestPssAllProcsLocked(now, false, mProcessStats.isMemFactorLowered());

        }

        // Update from any uid changes.

        for (int i=mActiveUids.size()-1; i>=0; i--) {

            final UidRecord uidRec = mActiveUids.valueAt(i);

            int uidChange = UidRecord.CHANGE_PROCSTATE;

            if (uidRec.setProcState != uidRec.curProcState) {

                if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,

                        "Changes in " + uidRec + ": proc state from " + uidRec.setProcState

                        + " to " + uidRec.curProcState);

                if (ActivityManager.isProcStateBackground(uidRec.curProcState)) {

                    if (!ActivityManager.isProcStateBackground(uidRec.setProcState)) {

                        uidRec.lastBackgroundTime = nowElapsed;

                        if (!mHandler.hasMessages(IDLE_UIDS_MSG)) {

                            // Note: the background settle time is in elapsed realtime, while

                            // the handler time base is uptime.  All this means is that we may

                            // stop background uids later than we had intended, but that only

                            // happens because the device was sleeping so we are okay anyway.

                            mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG, BACKGROUND_SETTLE_TIME);

                        }

                    }

                } else {

                    if (uidRec.idle) {

                        uidChange = UidRecord.CHANGE_ACTIVE;

                        uidRec.idle = false;

                    }

                    uidRec.lastBackgroundTime = 0;

                }

                uidRec.setProcState = uidRec.curProcState;

                enqueueUidChangeLocked(uidRec, -1, uidChange);

                noteUidProcessState(uidRec.uid, uidRec.curProcState);

            }

        }

        if (mProcessStats.shouldWriteNowLocked(now)) {

            mHandler.post(new Runnable() {

                @Override public void run() {

                    synchronized (ActivityManagerService.this) {

                        mProcessStats.writeStateAsyncLocked();

                    }

                }

            });

        }

        if (DEBUG_OOM_ADJ) {

            final long duration = SystemClock.uptimeMillis() - now;

            if (false) {

                Slog.d(TAG_OOM_ADJ, "Did OOM ADJ in " + duration + "ms",

                        new RuntimeException("here").fillInStackTrace());

            } else {

                Slog.d(TAG_OOM_ADJ, "Did OOM ADJ in " + duration + "ms");

            }

        }

    }

updateOomAdjLocked()方法通過調用computeOomAdjLocked()方法來計算程序的oom_adj的值。如果程序的curAdj變量的值仍然大于等于系統預定義的最大oom_adj值(UNKNOW_ADJ),則表明該程序屬于“cached”程序或者空程序,updateOomAdjLocked()方法将會為該程序配置設定oom_adj值。如果用來表示程序狀态的變量curProcState的值為PROCESS_STATE_CACHED_ACTIVITY或者PROCESS_STATE_CACHED_ACTIVITY_CLIENT,這說明程序是cached程序,否則為空程序。

updateOomAdjLocked()方法中根據系統定義的cached程序的最大和最小oom_adj的值,先計算出slot的數量,後計算每個slot需要容納的cached程序的數量cachedFactor和空程序的數量emptyFactor,這樣做的目的為了将系統中cached程序和空程序分成不同的級别,每個級别有相同的oom_adj值,級别和級别之間的oom_adj值為2.是以,updateOomAdjLocked()方法區分某個程序是cached程序還是空程序後,會按照從低到高的原則把程序放到某個級别中,如果該幾倍程序滿了,就進入下一個級别。