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

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

本文是Android進(jìn)程管理系列文章的第二篇,會(huì)講解進(jìn)程管理中的優(yōu)先級(jí)管理。

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

本文適合Android平臺(tái)的應(yīng)用程序開(kāi)發(fā)者,也適合對(duì)于Android系統(tǒng)內(nèi)部實(shí)現(xiàn)感興趣的讀者。

前言

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

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

  • 當(dāng)內(nèi)存緊張時(shí),系統(tǒng)對(duì)于進(jìn)程的回收策略
  • 系統(tǒng)對(duì)于進(jìn)程的CPU調(diào)度策略
  • 虛擬機(jī)對(duì)于進(jìn)程的內(nèi)存分配和垃圾回收策略

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

Processes and Threads (如果你還沒(méi)有閱讀,請(qǐng)立即閱讀一下這篇文章)一文中,我們已經(jīng)了解到,系統(tǒng)對(duì)于進(jìn)程的優(yōu)先級(jí)有如下五個(gè)分類:

  1. 前臺(tái)進(jìn)程
  2. 可見(jiàn)進(jìn)程
  3. 服務(wù)進(jìn)程
  4. 后臺(tái)進(jìn)程
  5. 空進(jìn)程

這只是一個(gè)粗略的劃分。其實(shí),在系統(tǒng)的內(nèi)部實(shí)現(xiàn)中,優(yōu)先級(jí)遠(yuǎn)不止這么五種。

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

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

  • 每一個(gè)Android的應(yīng)用進(jìn)程中,都可能包含四大組件(Activity,Service,ContentProviderBroadcastReceiver)中的一個(gè)/種或者多個(gè)/種。
  • 對(duì)于每一個(gè)應(yīng)用進(jìn)程,在ActivityManagerService中都有一個(gè)ProcessRecord對(duì)象與之對(duì)應(yīng)。

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

對(duì)于運(yùn)行中的Service和ContentProvider來(lái)說(shuō),可能有若干個(gè)客戶端進(jìn)程正在對(duì)其使用。

ProcessRecord中,詳細(xì)記錄了上面提到的這些信息,相關(guān)代碼如下:

// 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 記錄了進(jìn)程中運(yùn)行的Activity
  • services,executingServices 記錄了進(jìn)程中運(yùn)行的Service
  • receivers 記錄了進(jìn)程中運(yùn)行的BroadcastReceiver
  • pubProviders 記錄了進(jìn)程中運(yùn)行的ContentProvider

而:

  • connections 記錄了對(duì)于Service連接
  • conProviders 記錄了對(duì)于ContentProvider的連接

這里的連接是指一種使用關(guān)系,對(duì)于Service和ContentProvider是類似的。

它們都有可能同時(shí)被多個(gè)客戶端使用,每個(gè)使用的客戶端都需要記錄一個(gè)連接,和如下圖所示:


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

例如:當(dāng)一個(gè)后臺(tái)的Service正在被一個(gè)前臺(tái)的Activity使用,那么這個(gè)后臺(tái)的Service就需要設(shè)置一個(gè)較高的優(yōu)先級(jí)以便不會(huì)被回收。(否則后臺(tái)Service進(jìn)程一旦被回收,便會(huì)對(duì)前臺(tái)的Activity造成影響。)

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

  • Activity是否在前臺(tái),用戶是否可見(jiàn)
  • Service正在被哪些客戶端使用
  • ContentProvider正在被哪些客戶端使用
  • BroadcastReceiver是否正在接受廣播

優(yōu)先級(jí)的基礎(chǔ)

oom_score_adj

對(duì)于每一個(gè)運(yùn)行中的進(jìn)程,Linux內(nèi)核都通過(guò)proc文件系統(tǒng)暴露這樣一個(gè)文件來(lái)允許其他程序修改指定進(jìn)程的優(yōu)先級(jí):

/proc/[pid]/oom_score_adj。(修改這個(gè)文件的內(nèi)容需要root權(quán)限)

這個(gè)文件允許的值的范圍是:-1000 ~ +1000之間。值越小,表示進(jìn)程越重要

當(dāng)內(nèi)存非常緊張時(shí),系統(tǒng)便會(huì)遍歷所有進(jìn)程,以確定那個(gè)進(jìn)程需要被殺死以回收內(nèi)存,此時(shí)便會(huì)讀取oom_score_adj 這個(gè)文件的值。關(guān)于這個(gè)值的使用,在后面講解進(jìn)程回收的的時(shí)候,我們會(huì)詳細(xì)講解。

PS:在Linux 2.6.36之前的版本中,Linux 提供調(diào)整優(yōu)先級(jí)的文件是/proc/[pid]/oom_adj。這個(gè)文件允許的值的范圍是-17 ~ +15之間。數(shù)值越小表示進(jìn)程越重要。
這個(gè)文件在新版的Linux中已經(jīng)廢棄。

但你仍然可以使用這個(gè)文件,當(dāng)你修改這個(gè)文件的時(shí)候,內(nèi)核會(huì)直接進(jìn)行換算,將結(jié)果反映到oom_score_adj這個(gè)文件上。

Android早期版本的實(shí)現(xiàn)中也是依賴oom_adj這個(gè)文件。但是在新版本中,已經(jīng)切換到使用oom_score_adj這個(gè)文件。

ProcessRecord中下面這些屬性反應(yīng)了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 指定了該進(jìn)程允許的oom_score_adj最大值。這個(gè)屬性主要是給系統(tǒng)應(yīng)用和常駐內(nèi)存的進(jìn)程使用,這些進(jìn)程的優(yōu)先級(jí)的計(jì)算方法與應(yīng)用進(jìn)程的計(jì)算方法不一樣,通過(guò)設(shè)定maxAdj保證這些進(jìn)程一直擁有較高的優(yōu)先級(jí)(在后面”優(yōu)先級(jí)的算法“中,我們會(huì)看到對(duì)于這個(gè)屬性的使用)。

除此之外,還有四個(gè)屬性。

這其中,curXXX這一組記錄了這一次優(yōu)先級(jí)計(jì)算的結(jié)果。在計(jì)算完成之后,會(huì)將curXXX復(fù)制給對(duì)應(yīng)的setXXX這一組上進(jìn)行備份。
(下文的其他屬性也會(huì)看到curXXX和setXXX的形式,和這里的原理是一樣的。)

另外,xxxRawAdj記錄了沒(méi)有經(jīng)過(guò)限制的adj值,“沒(méi)有經(jīng)過(guò)限制”是指這其中的值可能是超過(guò)了oom_score_adj文件所允許的范圍(-1000 ~ 1000)。

為了便于管理,ProcessList.java中預(yù)定義了oom_score_adj的可能取值。

其實(shí)這里的預(yù)定義值也是對(duì)應(yīng)用進(jìn)程的一種分類,它們是:

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

這里我們看到,FOREGROUND_APP_ADJ = 0,這個(gè)是前臺(tái)應(yīng)用進(jìn)程的優(yōu)先級(jí)。這是用戶正在交互的應(yīng)用,它們是很重要的,系統(tǒng)不應(yīng)當(dāng)把它們回收了。

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

VISIBLE_APP_ADJ,PERCEPTIBLE_APP_ADJ,PREVIOUS_APP_ADJ這幾個(gè)級(jí)別的優(yōu)先級(jí)就逐步降低了。

VISIBLE_APP_ADJ是具有可見(jiàn)Activity進(jìn)程的優(yōu)先級(jí):同一時(shí)刻,不一定只有一個(gè)Activity是可見(jiàn)的,如果前臺(tái)Activity設(shè)置了透明屬性,那么背后的Activity也是可見(jiàn)的。

PERCEPTIBLE_APP_ADJ是指用戶可感知的進(jìn)程,可感知的進(jìn)程包括:

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

另外,PREVIOUS_APP_ADJ描述的是前一個(gè)應(yīng)用的優(yōu)先級(jí)。所謂“前一個(gè)應(yīng)用”是指:在啟動(dòng)新的Activity時(shí),如果新啟動(dòng)的Activity是屬于一個(gè)新的進(jìn)程的,那么當(dāng)前即將被stop的Activity所在的進(jìn)程便會(huì)成為“前一個(gè)應(yīng)用”進(jìn)程。

HEAVY_WEIGHT_APP_ADJ 描述的重量級(jí)進(jìn)程是指那些通過(guò)Manifest指明不能保存狀態(tài)的應(yīng)用進(jìn)程。

除此之外,Android系統(tǒng)中,有一些系統(tǒng)應(yīng)用會(huì)常駐內(nèi)存,這些應(yīng)用通常是系統(tǒng)實(shí)現(xiàn)的一部分,如果它們不存在,系統(tǒng)將處于比較奇怪的狀態(tài),例如SystemUI(狀態(tài)欄,Keyguard都處于這個(gè)應(yīng)用中)。

所以它們的優(yōu)先級(jí)比所有應(yīng)用進(jìn)程的優(yōu)先級(jí)更高:PERSISTENT_SERVICE_ADJ = -700,PERSISTENT_PROC_ADJ = -800

另外,還有一些系統(tǒng)服務(wù)的實(shí)現(xiàn),如果這些系統(tǒng)服務(wù)不存在,系統(tǒng)將無(wú)法工作,所以這些應(yīng)用的優(yōu)先級(jí)最高,幾乎是任何任何時(shí)候都需要存在的:SYSTEM_ADJ = -900,NATIVE_ADJ = -1000。

Schedule Group

運(yùn)行中的進(jìn)程會(huì)能夠獲取的CPU時(shí)間片可能是不一樣的,Linux本身提供了相關(guān)的API來(lái)調(diào)整,例如:sched_setscheduler

在ProcessRecord中,下面的屬性記錄了進(jìn)程的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)用來(lái)設(shè)置進(jìn)程的調(diào)度組。調(diào)度組會(huì)影響進(jìn)程的CPU占用時(shí)間。

Process State

ProcessRecord中的下面這幾個(gè)屬性記錄了進(jìn)程的狀態(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

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

這些屬性可能的取值定義在ActivityManager中,這些定義的注釋很好的說(shuō)明了這些值在什么時(shí)候會(huì)被用到:

/** @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)先級(jí)的更新

前文已經(jīng)提到,系統(tǒng)會(huì)對(duì)處于不同狀態(tài)的進(jìn)程設(shè)置不同的優(yōu)先級(jí)。但實(shí)際上,進(jìn)程的狀態(tài)是一直在變化中的。例如:用戶可以隨時(shí)會(huì)啟動(dòng)一個(gè)新的Activity,或者將一個(gè)前臺(tái)的Activity切換到后臺(tái)。在這個(gè)時(shí)候,發(fā)生狀態(tài)變化的Activity的所在進(jìn)程的優(yōu)先級(jí)就需要進(jìn)行更新。

并且,Activity可能會(huì)使用其他的Service或者Provider。當(dāng)Activity的進(jìn)程優(yōu)先級(jí)發(fā)生變化的時(shí)候,其所使用的Service或者Provider的優(yōu)先級(jí)也應(yīng)當(dāng)發(fā)生變化。

ActivityManagerService中有如下兩個(gè)方法用來(lái)更新進(jìn)程的優(yōu)先級(jí):

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

其中,第一個(gè)方法是針對(duì)指定的一個(gè)進(jìn)程更新優(yōu)先級(jí)。另一個(gè)是對(duì)所有運(yùn)行中的進(jìn)程更新優(yōu)先級(jí)。

在下面的這些情況下,需要對(duì)指定的應(yīng)用進(jìn)程更新優(yōu)先級(jí):

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

final boolean updateOomAdjLocked(ProcessRecord app) 被調(diào)用的關(guān)系如下圖所示:

在如下一些情況下,系統(tǒng)會(huì)對(duì)所有應(yīng)用進(jìn)程的優(yōu)先級(jí)進(jìn)行更新:

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

final void updateOomAdjLocked() 被調(diào)用的關(guān)系圖如下所示:

優(yōu)先級(jí)的算法

ActivityManagerService中的computeOomAdjLocked方法負(fù)責(zé)計(jì)算進(jìn)程的優(yōu)先級(jí)。

上文中已經(jīng)提到,優(yōu)先級(jí)計(jì)算的基礎(chǔ)主要就是依賴以下信息:

  • 一個(gè)進(jìn)程中可能包含四個(gè)組件中的一個(gè)/種或多個(gè)多個(gè)/種
  • 每個(gè)Service或者Provider的客戶端連接

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

下面我們來(lái)詳細(xì)看其中的每一個(gè)步驟:

  • 1.確認(rèn)該進(jìn)程是否是空進(jìn)程

    空進(jìn)程中沒(méi)有任何組件,因此主線程也為null(ProcessRecord.thread描述了應(yīng)用進(jìn)程的主線程)。

    如果是空進(jìn)程,則不需要再做后面的計(jì)算了。直接設(shè)置為ProcessList.CACHED_APP_MAX_ADJ級(jí)別即可。

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.確認(rèn)是否設(shè)置了maxAdj

    上文已經(jīng)提到過(guò),系統(tǒng)進(jìn)程或者Persistent進(jìn)程會(huì)通過(guò)設(shè)置maxAdj來(lái)保持其較高的優(yōu)先級(jí),對(duì)于這類進(jìn)程不用按照普通進(jìn)程的算法進(jìn)行計(jì)算,直接按照maxAdj的值設(shè)置即可。

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.確認(rèn)進(jìn)程中是否有前臺(tái)優(yōu)先級(jí)的組件

    前臺(tái)優(yōu)先級(jí)的組件是指:

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

    除此之外,還有Instrumentation被認(rèn)為是具有較高優(yōu)先級(jí)的。Instrumentation應(yīng)用是輔助測(cè)試用的,正常運(yùn)行的系統(tǒng)中不用考慮這種應(yīng)用。

    假設(shè)進(jìn)程中包含了以上提到的前臺(tái)優(yōu)先級(jí)的任何一個(gè)組件,則直接設(shè)置進(jìn)程優(yōu)先級(jí)為FOREGROUND_APP_ADJ即可。因?yàn)檫@已經(jīng)是應(yīng)用程序能夠獲取的最高優(yōu)先級(jí)了。

  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.確認(rèn)進(jìn)程中是否有較高優(yōu)先級(jí)的Activity

    這里需要遍歷進(jìn)程中的所有Activity,找出其中優(yōu)先級(jí)最高的設(shè)置為進(jìn)程的優(yōu)先級(jí)。

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

    1. 該Activity處于可見(jiàn)狀態(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.確認(rèn)進(jìn)程中是否有前臺(tái)Service

    通過(guò)startForeground啟動(dòng)的Service被認(rèn)為是前臺(tái)Service。給予這類進(jìn)程PERCEPTIBLE_APP_ADJ級(jí)別的優(yōu)先級(jí)。

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.確認(rèn)是否是特殊類型進(jìn)程

    特殊類型的進(jìn)程包括:重量級(jí)進(jìn)程,桌面進(jìn)程,前一個(gè)應(yīng)用進(jìn)程,正在執(zhí)行備份的進(jìn)程。
    重量級(jí)進(jìn)程和前一個(gè)應(yīng)用進(jìn)程在上文中已經(jīng)說(shuō)過(guò)了。

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的客戶端計(jì)算優(yōu)先級(jí)

    這里需要遍歷所有的Service,并且還需要遍歷每一個(gè)Service的所有連接。然后根據(jù)連接的關(guān)系確認(rèn)客戶端進(jìn)程的優(yōu)先級(jí)來(lái)確定當(dāng)前進(jìn)程的優(yōu)先級(jí)。

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

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的客戶端確認(rèn)優(yōu)先級(jí)

    這里與Service類似,需要遍歷所有的Provider,以及每一個(gè)Provider的所有連接。然后根據(jù)連接的關(guān)系確認(rèn)客戶端進(jìn)程的優(yōu)先級(jí)來(lái)確定當(dāng)前進(jìn)程的優(yōu)先級(jí)。

    類似的,ContentProviderConnection.client為客戶端進(jìn)程的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ù)進(jìn)程中的Service,Provider的一些特殊狀態(tài)做一些處理,另外還有針對(duì)空進(jìn)程以及設(shè)置了maxAdj的進(jìn)程做一些處理,這里就不貼出代碼了。

這里想專門(mén)說(shuō)明一下的是,在這一步還會(huì)對(duì)Service進(jìn)程做ServiceB的區(qū)分。相關(guān)代碼見(jiàn)下文。

系統(tǒng)將Service進(jìn)程分為ServiceA和ServiceB。ServiceA是相對(duì)來(lái)說(shuō)較新的Service,而ServiceB相對(duì)來(lái)說(shuō)是比較“老舊”的,對(duì)用戶來(lái)說(shuō)可能是不那么感興趣的,因此ServiceB的優(yōu)先級(jí)會(huì)相對(duì)低一些。

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

而ServiceB的標(biāo)準(zhǔn)是:app.serviceb = mNewNumAServiceProcs > (mNumServiceProcs/3);
即:所有Service進(jìn)程的前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.保存結(jié)果
    最終需要把本次的計(jì)算結(jié)果保存到ProcessRecord中:
app.curAdj = app.modifyRawOomAdj(adj);
app.curSchedGroup = schedGroup;
app.curProcState = procState;
app.foregroundActivities = foregroundActivities;

優(yōu)先級(jí)的生效

優(yōu)先級(jí)的生效是指:將計(jì)算出來(lái)的優(yōu)先級(jí)真正應(yīng)用到系統(tǒng)中,applyOomAdjLocked 方法負(fù)責(zé)了此項(xiàng)工作。

前文中我們提到,優(yōu)先級(jí)意味著三個(gè)方面,這里的生效就對(duì)應(yīng)了三個(gè)方面:

  1. ProcessList.setOomAdj(app.pid, app.info.uid, app.curAdj);
    將計(jì)算出來(lái)的adj值寫(xiě)入到procfs中,即:/proc/[pid]/oom_score_adj 這個(gè)文件中。在進(jìn)程回收的時(shí)候,這個(gè)值是被考慮的一個(gè)非常重要的因素,在下一篇文章中我們會(huì)詳細(xì)講解。

  2. Process.setProcessGroup(app.pid, processGroup);
    用來(lái)設(shè)置進(jìn)程的調(diào)度組。

  3. app.thread.setProcessState(app.repProcState);
    這個(gè)方法會(huì)最終調(diào)用到

VMRuntime.getRuntime().updateProcessState(dalvikProcessState);將進(jìn)程的狀態(tài)設(shè)置到虛擬機(jī)中。

結(jié)束語(yǔ)

前言中我們提到,“優(yōu)先級(jí)反應(yīng)了系統(tǒng)對(duì)于進(jìn)程重要性的判定?!?/p>

那么,系統(tǒng)如何評(píng)價(jià)進(jìn)程的優(yōu)先級(jí),便是系統(tǒng)本身一個(gè)很重要的特性。了解系統(tǒng)的這一特性對(duì)于我們開(kāi)發(fā)應(yīng)用程序,以及對(duì)于應(yīng)用程序運(yùn)行的行為分析是很有意義的。

系統(tǒng)在判定優(yōu)先級(jí)的時(shí)候,應(yīng)當(dāng)做到公平公正,并且不能讓開(kāi)發(fā)者有機(jī)可乘。

“公平公正”是指系統(tǒng)需要站在一個(gè)中間人的狀態(tài)下,不偏倚任何一個(gè)應(yīng)用,公正的將系統(tǒng)資源分配給真正需要的進(jìn)程。并且在系統(tǒng)資源緊張的時(shí)候,回收不重要的進(jìn)程。

通過(guò)上文的分析,我們看到,Android系統(tǒng)認(rèn)為“重要”的進(jìn)程主要有三類:

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

不過(guò)對(duì)于這一點(diǎn)是有改進(jìn)的空間的,例如,可以引入對(duì)于用戶習(xí)慣的分析:如果是用戶頻繁使用的應(yīng)用,可以給予這個(gè)應(yīng)用更高的優(yōu)先級(jí)來(lái)減少它們被回收的頻度,以提升這些應(yīng)用的效應(yīng)速度。畢竟,冷啟動(dòng)和熱啟動(dòng),響應(yīng)時(shí)間是差別很大的。

“不能讓開(kāi)發(fā)者有機(jī)可乘”是指:系統(tǒng)對(duì)于進(jìn)程優(yōu)先級(jí)的判定的因素應(yīng)當(dāng)是不能被開(kāi)發(fā)者利用的。因?yàn)橐坏╅_(kāi)發(fā)者可以利用,每個(gè)開(kāi)發(fā)者都肯定會(huì)將自己的設(shè)置為高優(yōu)先級(jí),來(lái)?yè)屨几嗟馁Y源。

需要說(shuō)明的是,Android在這個(gè)方面是存在缺陷的:在Android系統(tǒng)上,可以通過(guò)startForeground拿到前臺(tái)的優(yōu)先級(jí)的。后來(lái)Google也意識(shí)到這個(gè)問(wèn)題,于是在API Level 18以上的版本上,調(diào)用startForeground這個(gè)API會(huì)在通知欄顯示一條通知以告知用戶。但是,這個(gè)改進(jìn)是有Bug的:開(kāi)發(fā)者可以同時(shí)通過(guò)startForeground啟動(dòng)兩個(gè)Service,指定同樣的通知id,然后退出其中一個(gè),這樣應(yīng)用的不會(huì)在通知欄顯示通知圖標(biāo),并且拿到了前臺(tái)的優(yōu)先級(jí)。這個(gè)便是讓開(kāi)發(fā)者“有機(jī)可乘”了。

由于筆者認(rèn)為這不是一個(gè)很好的行為,具體的做法不細(xì)說(shuō)了,有興趣自己去網(wǎng)上搜索。

本文,我們?cè)敿?xì)講解的Android中進(jìn)程優(yōu)先級(jí)的計(jì)算方法,在下一篇文章中,我們會(huì)專門(mén)講解與進(jìn)程回收相關(guān)的內(nèi)容,敬請(qǐng)期待。

參考資料與推薦讀物

Embedded Android: Porting, Extending, and Customizing

The proc filesystem

sched_setscheduler

更多文章請(qǐng)關(guān)注公眾號(hào)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容