Android進程管理三部曲[2]-進程的優(yōu)先級

作者: 強波 (阿里云OS平臺部-Cloud Engine)
博客: http://qiangbo.space/

本文是Android進程管理系列文章的第二篇屡律,會講解進程管理中的優(yōu)先級管理谆棱。

進程管理的第一篇文章:《進程的創(chuàng)建

本文適合Android平臺的應用程序開發(fā)者,也適合對于Android系統(tǒng)內部實現(xiàn)感興趣的讀者嗓违。

前言

進程的優(yōu)先級反應了系統(tǒng)對于進程重要性的判定。

在Android系統(tǒng)中,進程的優(yōu)先級影響著以下三個因素:

  • 當內存緊張時缺虐,系統(tǒng)對于進程的回收策略
  • 系統(tǒng)對于進程的CPU調度策略
  • 虛擬機對于進程的內存分配和垃圾回收策略

本文會主要講解系統(tǒng)對于進程優(yōu)先級的判斷依據(jù)和計算方法。

Processes and Threads (如果你還沒有閱讀礁凡,請立即閱讀一下這篇文章)一文中高氮,我們已經了解到,系統(tǒng)對于進程的優(yōu)先級有如下五個分類:

  1. 前臺進程
  2. 可見進程
  3. 服務進程
  4. 后臺進程
  5. 空進程

這只是一個粗略的劃分把篓。其實纫溃,在系統(tǒng)的內部實現(xiàn)中,優(yōu)先級遠不止這么五種韧掩。

優(yōu)先級的依據(jù)

進程的創(chuàng)建一文中我們提到:

  • 每一個Android的應用進程中紊浩,都可能包含四大組件(ActivityService疗锐,ContentProvider坊谁,BroadcastReceiver)中的一個/種或者多個/種。
  • 對于每一個應用進程滑臊,在ActivityManagerService中都有一個ProcessRecord對象與之對應口芍。

而進程中四大組件的狀態(tài)就是決定進程優(yōu)先級的根本依據(jù)。

對于運行中的Service和ContentProvider來說雇卷,可能有若干個客戶端進程正在對其使用鬓椭。

ProcessRecord中颠猴,詳細記錄了上面提到的這些信息,相關代碼如下:

// all activities running in the process
final ArrayList<ActivityRecord> activities = new ArrayList<>();
// all ServiceRecord running in this process
final ArraySet<ServiceRecord> services = new ArraySet<>();
// services that are currently executing code (need to remain foreground).
final ArraySet<ServiceRecord> executingServices = new ArraySet<>();
// All ConnectionRecord this process holds
final ArraySet<ConnectionRecord> connections = new ArraySet<>();
// all IIntentReceivers that are registered from this process.
final ArraySet<ReceiverList> receivers = new ArraySet<>();
// class (String) -> ContentProviderRecord
final ArrayMap<String, ContentProviderRecord> pubProviders = new ArrayMap<>();
// All ContentProviderRecord process is using
final ArrayList<ContentProviderConnection> conProviders = new ArrayList<>();

這里的:

  • activities 記錄了進程中運行的Activity
  • services小染,executingServices 記錄了進程中運行的Service
  • receivers 記錄了進程中運行的BroadcastReceiver
  • pubProviders 記錄了進程中運行的ContentProvider

而:

  • connections 記錄了對于Service連接
  • conProviders 記錄了對于ContentProvider的連接

這里的連接是指一種使用關系翘瓮,對于Service和ContentProvider是類似的。

它們都有可能同時被多個客戶端使用裤翩,每個使用的客戶端都需要記錄一個連接资盅,和如下圖所示:


連接的意義在于:連接的客戶端的優(yōu)先級會影響被使用的Service和ContentProvider所在進程的優(yōu)先級。

例如:當一個后臺的Service正在被一個前臺的Activity使用踊赠,那么這個后臺的Service就需要設置一個較高的優(yōu)先級以便不會被回收呵扛。(否則后臺Service進程一旦被回收,便會對前臺的Activity造成影響筐带。)

這些組件的狀態(tài)就是進程優(yōu)先級的決定性因素今穿。 組件的狀態(tài)是指:

  • Activity是否在前臺,用戶是否可見
  • Service正在被哪些客戶端使用
  • ContentProvider正在被哪些客戶端使用
  • BroadcastReceiver是否正在接受廣播

優(yōu)先級的基礎

oom_score_adj

對于每一個運行中的進程烫堤,Linux內核都通過proc文件系統(tǒng)暴露這樣一個文件來允許其他程序修改指定進程的優(yōu)先級:

/proc/[pid]/oom_score_adj荣赶。(修改這個文件的內容需要root權限)

這個文件允許的值的范圍是:-1000 ~ +1000之間。值越小鸽斟,表示進程越重要拔创。

當內存非常緊張時,系統(tǒng)便會遍歷所有進程富蓄,以確定那個進程需要被殺死以回收內存剩燥,此時便會讀取oom_score_adj 這個文件的值。關于這個值的使用立倍,在后面講解進程回收的的時候灭红,我們會詳細講解。

PS:在Linux 2.6.36之前的版本中口注,Linux 提供調整優(yōu)先級的文件是/proc/[pid]/oom_adj变擒。這個文件允許的值的范圍是-17 ~ +15之間。數(shù)值越小表示進程越重要寝志。
這個文件在新版的Linux中已經廢棄娇斑。

但你仍然可以使用這個文件,當你修改這個文件的時候材部,內核會直接進行換算毫缆,將結果反映到oom_score_adj這個文件上。

Android早期版本的實現(xiàn)中也是依賴oom_adj這個文件乐导。但是在新版本中苦丁,已經切換到使用oom_score_adj這個文件。

ProcessRecord中下面這些屬性反應了oom_score_adj的值:

int maxAdj;                 // Maximum OOM adjustment for this process
int curRawAdj;              // Current OOM unlimited adjustment for this process
int setRawAdj;              // Last set OOM unlimited adjustment for this process
int curAdj;                 // Current OOM adjustment for this process
int setAdj;                 // Last set OOM adjustment for this process

maxAdj 指定了該進程允許的oom_score_adj最大值物臂。這個屬性主要是給系統(tǒng)應用和常駐內存的進程使用旺拉,這些進程的優(yōu)先級的計算方法與應用進程的計算方法不一樣产上,通過設定maxAdj保證這些進程一直擁有較高的優(yōu)先級(在后面”優(yōu)先級的算法“中,我們會看到對于這個屬性的使用)蛾狗。

除此之外蒂秘,還有四個屬性。

這其中淘太,curXXX這一組記錄了這一次優(yōu)先級計算的結果。在計算完成之后规丽,會將curXXX復制給對應的setXXX這一組上進行備份蒲牧。
(下文的其他屬性也會看到curXXX和setXXX的形式,和這里的原理是一樣的赌莺。)

另外冰抢,xxxRawAdj記錄了沒有經過限制的adj值,“沒有經過限制”是指這其中的值可能是超過了oom_score_adj文件所允許的范圍(-1000 ~ 1000)艘狭。

為了便于管理挎扰,ProcessList.java中預定義了oom_score_adj的可能取值。

其實這里的預定義值也是對應用進程的一種分類巢音,它們是:

static final int UNKNOWN_ADJ = 1001; // 未知進程
static final int PREVIOUS_APP_ADJ = 700; // 前一個應用
static final int HOME_APP_ADJ = 600; // 桌面進程
static final int SERVICE_ADJ = 500; // 包含了Service的進程
static final int HEAVY_WEIGHT_APP_ADJ = 400; // 重量級進程
static final int BACKUP_APP_ADJ = 300; // 備份應用進程
static final int PERCEPTIBLE_APP_ADJ = 200; // 可感知的進程
static final int VISIBLE_APP_ADJ = 100; // 可見進程
static final int VISIBLE_APP_LAYER_MAX = PERCEPTIBLE_APP_ADJ - VISIBLE_APP_ADJ - 1;
static final int FOREGROUND_APP_ADJ = 0; // 前臺進程
static final int PERSISTENT_SERVICE_ADJ = -700; // 常駐服務進程
static final int PERSISTENT_PROC_ADJ = -800; // 常駐應用進程
static final int SYSTEM_ADJ = -900; // 系統(tǒng)進程
static final int NATIVE_ADJ = -1000; // native系統(tǒng)進程

這里我們看到遵倦,FOREGROUND_APP_ADJ = 0,這個是前臺應用進程的優(yōu)先級官撼。這是用戶正在交互的應用梧躺,它們是很重要的,系統(tǒng)不應當把它們回收了傲绣。

FOREGROUND_APP_ADJ = 0是普通應用程序能夠獲取到的最高優(yōu)先級掠哥。

VISIBLE_APP_ADJPERCEPTIBLE_APP_ADJ秃诵,PREVIOUS_APP_ADJ這幾個級別的優(yōu)先級就逐步降低了续搀。

VISIBLE_APP_ADJ是具有可見Activity進程的優(yōu)先級:同一時刻,不一定只有一個Activity是可見的菠净,如果前臺Activity設置了透明屬性禁舷,那么背后的Activity也是可見的。

PERCEPTIBLE_APP_ADJ是指用戶可感知的進程嗤练,可感知的進程包括:

  • 進程中包含了處于pause狀態(tài)或者正在pause的Activity
  • 進程中包含了正在stop的Activity
  • 進程中包含了前臺的Service

另外榛了,PREVIOUS_APP_ADJ描述的是前一個應用的優(yōu)先級。所謂“前一個應用”是指:在啟動新的Activity時煞抬,如果新啟動的Activity是屬于一個新的進程的霜大,那么當前即將被stop的Activity所在的進程便會成為“前一個應用”進程。

HEAVY_WEIGHT_APP_ADJ 描述的重量級進程是指那些通過Manifest指明不能保存狀態(tài)的應用進程革答。

除此之外战坤,Android系統(tǒng)中曙强,有一些系統(tǒng)應用會常駐內存,這些應用通常是系統(tǒng)實現(xiàn)的一部分途茫,如果它們不存在碟嘴,系統(tǒng)將處于比較奇怪的狀態(tài),例如SystemUI(狀態(tài)欄囊卜,Keyguard都處于這個應用中)娜扇。

所以它們的優(yōu)先級比所有應用進程的優(yōu)先級更高:PERSISTENT_SERVICE_ADJ = -700允粤,PERSISTENT_PROC_ADJ = -800庶诡。

另外袭厂,還有一些系統(tǒng)服務的實現(xiàn)陨囊,如果這些系統(tǒng)服務不存在坞淮,系統(tǒng)將無法工作摆寄,所以這些應用的優(yōu)先級最高靶衍,幾乎是任何任何時候都需要存在的:SYSTEM_ADJ = -900酪我,NATIVE_ADJ = -1000司浪。

Schedule Group

運行中的進程會能夠獲取的CPU時間片可能是不一樣的泊业,Linux本身提供了相關的API來調整,例如:sched_setscheduler啊易。

在ProcessRecord中吁伺,下面的屬性記錄了進程的Schedule Group:

int curSchedGroup;          // Currently desired scheduling class
int setSchedGroup;          // Last set to background scheduling class

它們可能的取值定義在Process.java中:

/**
* Default thread group -
* has meaning with setProcessGroup() only, cannot be used with setThreadGroup().
* When used with setProcessGroup(), the group of each thread in the process
* is conditionally changed based on that thread's current priority, as follows:
* threads with priority numerically less than THREAD_PRIORITY_BACKGROUND
* are moved to foreground thread group.  All other threads are left unchanged.
* @hide
*/
public static final int THREAD_GROUP_DEFAULT = -1;

/**
* Background thread group - All threads in
* this group are scheduled with a reduced share of the CPU.
* Value is same as constant SP_BACKGROUND of enum SchedPolicy.
* FIXME rename to THREAD_GROUP_BACKGROUND.
* @hide
*/
public static final int THREAD_GROUP_BG_NONINTERACTIVE = 0;

/**
* Foreground thread group - All threads in
* this group are scheduled with a normal share of the CPU.
* Value is same as constant SP_FOREGROUND of enum SchedPolicy.
* Not used at this level.
* @hide
**/
private static final int THREAD_GROUP_FOREGROUND = 1;

在Android中,Process.setProcessGroup(int pid, int group)用來設置進程的調度組认罩。調度組會影響進程的CPU占用時間箱蝠。

Process State

ProcessRecord中的下面這幾個屬性記錄了進程的狀態(tài):

int curProcState; // Currently computed process state
int repProcState; // Last reported process state
int setProcState; // Last set process state in process tracker
int pssProcState; // Currently requesting pss for

進程的狀態(tài)會影響虛擬機對于進程的內存分配和垃圾回收策略。

這些屬性可能的取值定義在ActivityManager中垦垂,這些定義的注釋很好的說明了這些值在什么時候會被用到:

/** @hide Process does not exist. */
public static final int PROCESS_STATE_NONEXISTENT = -1;
/** @hide Process is a persistent system process. */
public static final int PROCESS_STATE_PERSISTENT = 0;
/** @hide Process is a persistent system process and is doing UI. */
public static final int PROCESS_STATE_PERSISTENT_UI = 1;
/** @hide Process is hosting the current top activities.  Note that this covers
* all activities that are visible to the user. */
public static final int PROCESS_STATE_TOP = 2;
/** @hide Process is hosting a foreground service due to a system binding. */
public static final int PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 3;
/** @hide Process is hosting a foreground service. */
public static final int PROCESS_STATE_FOREGROUND_SERVICE = 4;
/** @hide Same as {@link #PROCESS_STATE_TOP} but while device is sleeping. */
public static final int PROCESS_STATE_TOP_SLEEPING = 5;
/** @hide Process is important to the user, and something they are aware of. */
public static final int PROCESS_STATE_IMPORTANT_FOREGROUND = 6;
/** @hide Process is important to the user, but not something they are aware of. */
public static final int PROCESS_STATE_IMPORTANT_BACKGROUND = 7;
/** @hide Process is in the background running a backup/restore operation. */
public static final int PROCESS_STATE_BACKUP = 8;
/** @hide Process is in the background, but it can't restore its state so we want
* to try to avoid killing it. */
public static final int PROCESS_STATE_HEAVY_WEIGHT = 9;
/** @hide Process is in the background running a service.  Unlike oom_adj, this level
* is used for both the normal running in background state and the executing
* operations state. */
public static final int PROCESS_STATE_SERVICE = 10;
/** @hide Process is in the background running a receiver.   Note that from the
* perspective of oom_adj receivers run at a higher foreground level, but for our
* prioritization here that is not necessary and putting them below services means
* many fewer changes in some process states as they receive broadcasts. */
public static final int PROCESS_STATE_RECEIVER = 11;
/** @hide Process is in the background but hosts the home activity. */
public static final int PROCESS_STATE_HOME = 12;
/** @hide Process is in the background but hosts the last shown activity. */
public static final int PROCESS_STATE_LAST_ACTIVITY = 13;
/** @hide Process is being cached for later use and contains activities. */
public static final int PROCESS_STATE_CACHED_ACTIVITY = 14;
/** @hide Process is being cached for later use and is a client of another cached
* process that contains activities. */
public static final int PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 15;
/** @hide Process is being cached for later use and is empty. */
public static final int PROCESS_STATE_CACHED_EMPTY = 16;

優(yōu)先級的更新

前文已經提到宦搬,系統(tǒng)會對處于不同狀態(tài)的進程設置不同的優(yōu)先級。但實際上劫拗,進程的狀態(tài)是一直在變化中的间校。例如:用戶可以隨時會啟動一個新的Activity,或者將一個前臺的Activity切換到后臺页慷。在這個時候憔足,發(fā)生狀態(tài)變化的Activity的所在進程的優(yōu)先級就需要進行更新。

并且酒繁,Activity可能會使用其他的Service或者Provider滓彰。當Activity的進程優(yōu)先級發(fā)生變化的時候,其所使用的Service或者Provider的優(yōu)先級也應當發(fā)生變化州袒。

ActivityManagerService中有如下兩個方法用來更新進程的優(yōu)先級:

  • final boolean updateOomAdjLocked(ProcessRecord app)
  • final void updateOomAdjLocked()

其中揭绑,第一個方法是針對指定的一個進程更新優(yōu)先級。另一個是對所有運行中的進程更新優(yōu)先級。

在下面的這些情況下他匪,需要對指定的應用進程更新優(yōu)先級:

  • 當有一個新的進程開始使用本進程中的ContentProvider
  • 當本進程中的一個Service被其他進程bind或者unbind
  • 當本進程中的Service的執(zhí)行完成或者退出了
  • 當本進程中一個BroadcastReceiver正在接受廣播
  • 當本進程中的BackUpAgent啟動或者退出了

final boolean updateOomAdjLocked(ProcessRecord app) 被調用的關系如下圖所示:

在如下一些情況下菇存,系統(tǒng)會對所有應用進程的優(yōu)先級進行更新:

  • 當有一個新的進程啟動時
  • 當有一個進程退出時
  • 當系統(tǒng)在清理后臺進程時
  • 當有一個進程被標記為前臺進程時
  • 當有一個進程進入或者退出cached狀態(tài)時
  • 當系統(tǒng)鎖屏或者解鎖時
  • 當有一個Activity啟動或者退出時
  • 當系統(tǒng)正在處理一個廣播事件時
  • 當前臺Activity發(fā)生改變時
  • 當有一個Service啟動時

final void updateOomAdjLocked() 被調用的關系圖如下所示:

優(yōu)先級的算法

ActivityManagerService中的computeOomAdjLocked方法負責計算進程的優(yōu)先級。

上文中已經提到邦蜜,優(yōu)先級計算的基礎主要就是依賴以下信息:

  • 一個進程中可能包含四個組件中的一個/種或多個多個/種
  • 每個Service或者Provider的客戶端連接

computeOomAdjLocked方法總計約700行依鸥,這個方法的執(zhí)行流程主要包含如下10個步驟:

下面我們來詳細看其中的每一個步驟:

  • 1.確認該進程是否是空進程

    空進程中沒有任何組件,因此主線程也為null(ProcessRecord.thread描述了應用進程的主線程)悼沈。

    如果是空進程贱迟,則不需要再做后面的計算了。直接設置為ProcessList.CACHED_APP_MAX_ADJ級別即可絮供。

if (app.thread == null) {
      app.adjSeq = mAdjSeq;
      app.curSchedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
      app.curProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
      return (app.curAdj=app.curRawAdj=ProcessList.CACHED_APP_MAX_ADJ);
}
  • 2.確認是否設置了maxAdj

    上文已經提到過关筒,系統(tǒng)進程或者Persistent進程會通過設置maxAdj來保持其較高的優(yōu)先級,對于這類進程不用按照普通進程的算法進行計算杯缺,直接按照maxAdj的值設置即可。

if (app.maxAdj <= ProcessList.FOREGROUND_APP_ADJ) {
      app.adjType = "fixed";
      app.adjSeq = mAdjSeq;
      app.curRawAdj = app.maxAdj;
      app.foregroundActivities = false;
      app.curSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
      app.curProcState = ActivityManager.PROCESS_STATE_PERSISTENT;
      app.systemNoUi = true;
      if (app == TOP_APP) {
          app.systemNoUi = false;
          app.curSchedGroup = ProcessList.SCHED_GROUP_TOP_APP;
          app.adjType = "pers-top-activity";
      } else if (activitiesSize > 0) {
          for (int j = 0; j < activitiesSize; j++) {
              final ActivityRecord r = app.activities.get(j);
              if (r.visible) {
                  app.systemNoUi = false;
              }
          }
      }
      if (!app.systemNoUi) {
          app.curProcState = ActivityManager.PROCESS_STATE_PERSISTENT_UI;
      }
      return (app.curAdj=app.maxAdj);
  }
  • 3.確認進程中是否有前臺優(yōu)先級的組件

    前臺優(yōu)先級的組件是指:

    1.前臺的Activity; 2.正在接受廣播的Receiver; 3.正在執(zhí)行任務的Service;

    除此之外睡榆,還有Instrumentation被認為是具有較高優(yōu)先級的萍肆。Instrumentation應用是輔助測試用的,正常運行的系統(tǒng)中不用考慮這種應用胀屿。

    假設進程中包含了以上提到的前臺優(yōu)先級的任何一個組件塘揣,則直接設置進程優(yōu)先級為FOREGROUND_APP_ADJ即可。因為這已經是應用程序能夠獲取的最高優(yōu)先級了宿崭。

  int adj;
  int schedGroup;
  int procState;
  boolean foregroundActivities = false;
  BroadcastQueue queue;
  if (app == TOP_APP) {
      adj = ProcessList.FOREGROUND_APP_ADJ;
      schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
      app.adjType = "top-activity";
      foregroundActivities = true;
      procState = PROCESS_STATE_CUR_TOP;
  } else if (app.instrumentationClass != null) {
      adj = ProcessList.FOREGROUND_APP_ADJ;
      schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
      app.adjType = "instrumentation";
      procState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
  } else if ((queue = isReceivingBroadcast(app)) != null) {
      adj = ProcessList.FOREGROUND_APP_ADJ;
      schedGroup = (queue == mFgBroadcastQueue)
              ? ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND;
      app.adjType = "broadcast";
      procState = ActivityManager.PROCESS_STATE_RECEIVER;
  } else if (app.executingServices.size() > 0) {
      adj = ProcessList.FOREGROUND_APP_ADJ;
      schedGroup = app.execServicesFg ?
              ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND;
      app.adjType = "exec-service";
      procState = ActivityManager.PROCESS_STATE_SERVICE;
  } else {
      schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
      adj = cachedAdj;
      procState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
      app.cached = true;
      app.empty = true;
      app.adjType = "cch-empty";
  }
  • 4.確認進程中是否有較高優(yōu)先級的Activity

    這里需要遍歷進程中的所有Activity亲铡,找出其中優(yōu)先級最高的設置為進程的優(yōu)先級。

    即便Activity不是前臺Activity葡兑,但是處于下面這些狀態(tài)的Activity優(yōu)先級也是被認為是較高優(yōu)先級的:

    1. 該Activity處于可見狀態(tài)
    2. 該Activity處于Pause正在Pause狀態(tài)
    3. 該Activity正在stop
if (!foregroundActivities && activitiesSize > 0) {
 int minLayer = ProcessList.VISIBLE_APP_LAYER_MAX;
 for (int j = 0; j < activitiesSize; j++) {
     final ActivityRecord r = app.activities.get(j);
     if (r.app != app) {
         Log.e(TAG, "Found activity " + r + " in proc activity list using " + r.app
                 + " instead of expected " + app);
         if (r.app == null || (r.app.uid == app.uid)) {
             // Only fix things up when they look sane
             r.app = app;
         } else {
             continue;
         }
     }
     if (r.visible) {
         // App has a visible activity; only upgrade adjustment.
         if (adj > ProcessList.VISIBLE_APP_ADJ) {
             adj = ProcessList.VISIBLE_APP_ADJ;
             app.adjType = "visible";
         }
         if (procState > PROCESS_STATE_CUR_TOP) {
             procState = PROCESS_STATE_CUR_TOP;
         }
         schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
         app.cached = false;
         app.empty = false;
         foregroundActivities = true;
         if (r.task != null && minLayer > 0) {
             final int layer = r.task.mLayerRank;
             if (layer >= 0 && minLayer > layer) {
                 minLayer = layer;
             }
         }
         break;
     } else if (r.state == ActivityState.PAUSING || r.state == ActivityState.PAUSED) {
         if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
             adj = ProcessList.PERCEPTIBLE_APP_ADJ;
             app.adjType = "pausing";
         }
         if (procState > PROCESS_STATE_CUR_TOP) {
             procState = PROCESS_STATE_CUR_TOP;
         }
         schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
         app.cached = false;
         app.empty = false;
         foregroundActivities = true;
     } else if (r.state == ActivityState.STOPPING) {
         if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
             adj = ProcessList.PERCEPTIBLE_APP_ADJ;
             app.adjType = "stopping";
         }
         if (!r.finishing) {
             if (procState > ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
                 procState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
             }
         }
         app.cached = false;
         app.empty = false;
         foregroundActivities = true;
     } else {
         if (procState > ActivityManager.PROCESS_STATE_CACHED_ACTIVITY) {
             procState = ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
             app.adjType = "cch-act";
         }
     }
 }
 if (adj == ProcessList.VISIBLE_APP_ADJ) {
     adj += minLayer;
 }
}
  • 5.確認進程中是否有前臺Service

    通過startForeground啟動的Service被認為是前臺Service奖蔓。給予這類進程PERCEPTIBLE_APP_ADJ級別的優(yōu)先級。

if (adj > ProcessList.PERCEPTIBLE_APP_ADJ
     || procState > ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
 if (app.foregroundServices) {
     // The user is aware of this app, so make it visible.
     adj = ProcessList.PERCEPTIBLE_APP_ADJ;
     procState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
     app.cached = false;
     app.adjType = "fg-service";
     schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
 } else if (app.forcingToForeground != null) {
     // The user is aware of this app, so make it visible.
     adj = ProcessList.PERCEPTIBLE_APP_ADJ;
     procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
     app.cached = false;
     app.adjType = "force-fg";
     app.adjSource = app.forcingToForeground;
     schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
 }
}
  • 6.確認是否是特殊類型進程

    特殊類型的進程包括:重量級進程讹堤,桌面進程吆鹤,前一個應用進程,正在執(zhí)行備份的進程洲守。
    重量級進程和前一個應用進程在上文中已經說過了疑务。

if (app == mHeavyWeightProcess) {
 if (adj > ProcessList.HEAVY_WEIGHT_APP_ADJ) {
     adj = ProcessList.HEAVY_WEIGHT_APP_ADJ;
     schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
     app.cached = false;
     app.adjType = "heavy";
 }
 if (procState > ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) {
     procState = ActivityManager.PROCESS_STATE_HEAVY_WEIGHT;
 }
}
    
if (app == mHomeProcess) {
 if (adj > ProcessList.HOME_APP_ADJ) {
     adj = ProcessList.HOME_APP_ADJ;
     schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
     app.cached = false;
     app.adjType = "home";
 }
 if (procState > ActivityManager.PROCESS_STATE_HOME) {
     procState = ActivityManager.PROCESS_STATE_HOME;
 }
}
    
if (app == mPreviousProcess && app.activities.size() > 0) {
 if (adj > ProcessList.PREVIOUS_APP_ADJ) {
     adj = ProcessList.PREVIOUS_APP_ADJ;
     schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
     app.cached = false;
     app.adjType = "previous";
 }
 if (procState > ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
     procState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
 }
}
    
if (false) Slog.i(TAG, "OOM " + app + ": initial adj=" + adj
     + " reason=" + app.adjType);
    
app.adjSeq = mAdjSeq;
app.curRawAdj = adj;
app.hasStartedServices = false;
    
if (mBackupTarget != null && app == mBackupTarget.app) {
 if (adj > ProcessList.BACKUP_APP_ADJ) {
     if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "oom BACKUP_APP_ADJ for " + app);
     adj = ProcessList.BACKUP_APP_ADJ;
     if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND) {
         procState = ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND;
     }
     app.adjType = "backup";
     app.cached = false;
 }
 if (procState > ActivityManager.PROCESS_STATE_BACKUP) {
     procState = ActivityManager.PROCESS_STATE_BACKUP;
 }
}
  • 7.根據(jù)所有Service的客戶端計算優(yōu)先級

    這里需要遍歷所有的Service,并且還需要遍歷每一個Service的所有連接梗醇。然后根據(jù)連接的關系確認客戶端進程的優(yōu)先級來確定當前進程的優(yōu)先級知允。

    ConnectionRecord.binding.client即為客戶端進程ProcessRecord,由此便可以知道客戶端進程的優(yōu)先級叙谨。

for (int is = app.services.size()-1;
     is >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
             || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
             || procState > ActivityManager.PROCESS_STATE_TOP);
     is--) {
 ServiceRecord s = app.services.valueAt(is);
 if (s.startRequested) {
     app.hasStartedServices = true;
     if (procState > ActivityManager.PROCESS_STATE_SERVICE) {
         procState = ActivityManager.PROCESS_STATE_SERVICE;
     }
     if (app.hasShownUi && app != mHomeProcess) {
         if (adj > ProcessList.SERVICE_ADJ) {
             app.adjType = "cch-started-ui-services";
         }
     } else {
         if (now < (s.lastActivity + ActiveServices.MAX_SERVICE_INACTIVITY)) {
             if (adj > ProcessList.SERVICE_ADJ) {
                 adj = ProcessList.SERVICE_ADJ;
                 app.adjType = "started-services";
                 app.cached = false;
             }
         }
         if (adj > ProcessList.SERVICE_ADJ) {
             app.adjType = "cch-started-services";
         }
     }
 }
    
 for (int conni = s.connections.size()-1;
         conni >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
                 || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
                 || procState > ActivityManager.PROCESS_STATE_TOP);
         conni--) {
     ArrayList<ConnectionRecord> clist = s.connections.valueAt(conni);
     for (int i = 0;
             i < clist.size() && (adj > ProcessList.FOREGROUND_APP_ADJ
                     || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
                     || procState > ActivityManager.PROCESS_STATE_TOP);
  • 8.根據(jù)所有Provider的客戶端確認優(yōu)先級

    這里與Service類似温鸽,需要遍歷所有的Provider,以及每一個Provider的所有連接唉俗。然后根據(jù)連接的關系確認客戶端進程的優(yōu)先級來確定當前進程的優(yōu)先級嗤朴。

    類似的配椭,ContentProviderConnection.client為客戶端進程的ProcessRecord

for (int provi = app.pubProviders.size()-1;
     provi >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
             || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
             || procState > ActivityManager.PROCESS_STATE_TOP);
     provi--) {
 ContentProviderRecord cpr = app.pubProviders.valueAt(provi);
 for (int i = cpr.connections.size()-1;
         i >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
                 || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
                 || procState > ActivityManager.PROCESS_STATE_TOP);
         i--) {
     ContentProviderConnection conn = cpr.connections.get(i);
     ProcessRecord client = conn.client;
     if (client == app) {
         // Being our own client is not interesting.
         continue;
     }
     int clientAdj = computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now);
     ...
  • 9.收尾工作
    收尾工作主要是根據(jù)進程中的Service雹姊,Provider的一些特殊狀態(tài)做一些處理股缸,另外還有針對空進程以及設置了maxAdj的進程做一些處理,這里就不貼出代碼了吱雏。

這里想專門說明一下的是敦姻,在這一步還會對Service進程做ServiceB的區(qū)分。相關代碼見下文歧杏。

系統(tǒng)將Service進程分為ServiceA和ServiceB镰惦。ServiceA是相對來說較新的Service,而ServiceB相對來說是比較“老舊”的犬绒,對用戶來說可能是不那么感興趣的旺入,因此ServiceB的優(yōu)先級會相對低一些。

static final int SERVICE_B_ADJ = 800;
static final int SERVICE_ADJ = 500;

而ServiceB的標準是:app.serviceb = mNewNumAServiceProcs > (mNumServiceProcs/3);
即:所有Service進程的前1/3為ServiceA凯力,剩下為ServiceB茵瘾。

if (adj == ProcessList.SERVICE_ADJ) {
  if (doingAll) {
      app.serviceb = mNewNumAServiceProcs > (mNumServiceProcs/3);
      mNewNumServiceProcs++;
      if (!app.serviceb) {
          if (mLastMemoryLevel > ProcessStats.ADJ_MEM_FACTOR_NORMAL
                  && app.lastPss >= mProcessList.getCachedRestoreThresholdKb()) {
              app.serviceHighRam = true;
              app.serviceb = true;
          } else {
              mNewNumAServiceProcs++;
          }
      } else {
          app.serviceHighRam = false;
      }
  }
  if (app.serviceb) {
      adj = ProcessList.SERVICE_B_ADJ;
  }
}

app.curRawAdj = adj;
  • 10.保存結果
    最終需要把本次的計算結果保存到ProcessRecord中:
app.curAdj = app.modifyRawOomAdj(adj);
app.curSchedGroup = schedGroup;
app.curProcState = procState;
app.foregroundActivities = foregroundActivities;

優(yōu)先級的生效

優(yōu)先級的生效是指:將計算出來的優(yōu)先級真正應用到系統(tǒng)中,applyOomAdjLocked 方法負責了此項工作咐鹤。

前文中我們提到拗秘,優(yōu)先級意味著三個方面,這里的生效就對應了三個方面:

  1. ProcessList.setOomAdj(app.pid, app.info.uid, app.curAdj);
    將計算出來的adj值寫入到procfs中祈惶,即:/proc/[pid]/oom_score_adj 這個文件中雕旨。在進程回收的時候,這個值是被考慮的一個非常重要的因素捧请,在下一篇文章中我們會詳細講解凡涩。

  2. Process.setProcessGroup(app.pid, processGroup);
    用來設置進程的調度組。

  3. app.thread.setProcessState(app.repProcState);
    這個方法會最終調用到

VMRuntime.getRuntime().updateProcessState(dalvikProcessState);將進程的狀態(tài)設置到虛擬機中疹蛉。

結束語

前言中我們提到突照,“優(yōu)先級反應了系統(tǒng)對于進程重要性的判定⊙跬拢”

那么讹蘑,系統(tǒng)如何評價進程的優(yōu)先級,便是系統(tǒng)本身一個很重要的特性筑舅。了解系統(tǒng)的這一特性對于我們開發(fā)應用程序座慰,以及對于應用程序運行的行為分析是很有意義的。

系統(tǒng)在判定優(yōu)先級的時候翠拣,應當做到公平公正版仔,并且不能讓開發(fā)者有機可乘。

“公平公正”是指系統(tǒng)需要站在一個中間人的狀態(tài)下,不偏倚任何一個應用蛮粮,公正的將系統(tǒng)資源分配給真正需要的進程益缎。并且在系統(tǒng)資源緊張的時候,回收不重要的進程然想。

通過上文的分析莺奔,我們看到,Android系統(tǒng)認為“重要”的進程主要有三類:

  1. 系統(tǒng)進程
  2. 前臺與用戶交互的進程
  3. 前臺進程所使用到的進程

不過對于這一點是有改進的空間的变泄,例如令哟,可以引入對于用戶習慣的分析:如果是用戶頻繁使用的應用,可以給予這個應用更高的優(yōu)先級來減少它們被回收的頻度妨蛹,以提升這些應用的效應速度屏富。畢竟,冷啟動和熱啟動蛙卤,響應時間是差別很大的狠半。

“不能讓開發(fā)者有機可乘”是指:系統(tǒng)對于進程優(yōu)先級的判定的因素應當是不能被開發(fā)者利用的。因為一旦開發(fā)者可以利用颤难,每個開發(fā)者都肯定會將自己的設置為高優(yōu)先級典予,來搶占更多的資源。

需要說明的是乐严,Android在這個方面是存在缺陷的:在Android系統(tǒng)上,可以通過startForeground拿到前臺的優(yōu)先級的衣摩。后來Google也意識到這個問題昂验,于是在API Level 18以上的版本上,調用startForeground這個API會在通知欄顯示一條通知以告知用戶艾扮。但是既琴,這個改進是有Bug的:開發(fā)者可以同時通過startForeground啟動兩個Service,指定同樣的通知id泡嘴,然后退出其中一個甫恩,這樣應用的不會在通知欄顯示通知圖標,并且拿到了前臺的優(yōu)先級酌予。這個便是讓開發(fā)者“有機可乘”了磺箕。

由于筆者認為這不是一個很好的行為,具體的做法不細說了抛虫,有興趣自己去網上搜索松靡。

本文,我們詳細講解的Android中進程優(yōu)先級的計算方法建椰,在下一篇文章中雕欺,我們會專門講解與進程回收相關的內容,敬請期待。

參考資料與推薦讀物

Embedded Android: Porting, Extending, and Customizing

The proc filesystem

sched_setscheduler

更多文章請關注公眾號

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末屠列,一起剝皮案震驚了整個濱河市啦逆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌笛洛,老刑警劉巖夏志,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異撞蜂,居然都是意外死亡盲镶,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門蝌诡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來溉贿,“玉大人,你說我怎么就攤上這事浦旱∮钌” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵颁湖,是天一觀的道長宣蠕。 經常有香客問我,道長甥捺,這世上最難降的妖魔是什么抢蚀? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮镰禾,結果婚禮上皿曲,老公的妹妹穿的比我還像新娘。我一直安慰自己吴侦,他們只是感情好屋休,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著备韧,像睡著了一般劫樟。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上织堂,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天叠艳,我揣著相機與錄音,去河邊找鬼易阳。 笑死虑绵,一個胖子當著我的面吹牛,可吹牛的內容都是我干的闽烙。 我是一名探鬼主播翅睛,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼声搁,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了捕发?” 一聲冷哼從身側響起疏旨,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎扎酷,沒想到半個月后檐涝,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡法挨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年谁榜,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片凡纳。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡窃植,死狀恐怖,靈堂內的尸體忽然破棺而出荐糜,到底是詐尸還是另有隱情巷怜,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布暴氏,位于F島的核電站延塑,受9級特大地震影響,放射性物質發(fā)生泄漏答渔。R本人自食惡果不足惜关带,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望沼撕。 院中可真熱鬧宋雏,春花似錦、人聲如沸端朵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽冲呢。三九已至,卻和暖如春招狸,著一層夾襖步出監(jiān)牢的瞬間敬拓,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工裙戏, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留乘凸,地道東北人。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓累榜,卻偏偏與公主長得像营勤,于是被迫代替她去往敵國和親灵嫌。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內容