本文基于原生 Android 9.0 源碼來解讀進(jìn)程優(yōu)先級原理.
一芋肠、概述
1.1 進(jìn)程
Android 框架對進(jìn)程創(chuàng)建與管理進(jìn)行了封裝,對于 APP開發(fā)者只需知道 Android 四大組件的使用央串。當(dāng) Activity, Service, ContentProvider, BroadcastReceiver 任一組件啟動時,當(dāng)其所承載的進(jìn)程存在則直接使用,不存在則由框架代碼自動調(diào)用 startProcessLocked 創(chuàng)建進(jìn)程锋叨。一個 APP 可以擁有多個進(jìn)程菊匿,多個 APP 也可以運(yùn)行在同一個進(jìn)程付呕,通過配置 Android:process 屬性來決定。所以說對 APP 來說進(jìn)程幾乎是透明的捧请,但了解進(jìn)程對于深刻理解 Android 系統(tǒng)是至關(guān)關(guān)鍵的凡涩。
1.2 優(yōu)先級
Android 系統(tǒng)的設(shè)計理念正是希望應(yīng)用進(jìn)程能盡量長時間地存活,以提升用戶體驗(yàn)疹蛉。應(yīng)用首次打開比較慢活箕,這個過程有進(jìn)程創(chuàng)建以及 Application 等信息的初始化,所以應(yīng)用在啟動之后可款,即便退到后臺并非立刻殺死育韩,而是存活一段時間枢纠,這樣下次再使用則會非忱蠲#快。對于APP同樣希望自身盡可能存活更長的時間派任,甚至探索各種泵校活黑科技悉罕。物極必反,系統(tǒng)處于低內(nèi)存的狀態(tài)下立镶,手機(jī)性能會有所下降壁袄;系統(tǒng)繼續(xù)放任所有進(jìn)程一直存活,系統(tǒng)內(nèi)存很快就會枯竭而亡媚媒,那么需要合理地進(jìn)程回收機(jī)制嗜逻。
到底該回收哪個進(jìn)程呢?系統(tǒng)根據(jù)進(jìn)程的組件狀態(tài)來決定每個進(jìn)程的優(yōu)先級值 ADJ缭召,系統(tǒng)根據(jù)一定策略先殺優(yōu)先級最低的進(jìn)程栈顷,然后逐步殺優(yōu)先級更低的進(jìn)程逆日,依此類推,以回收預(yù)期的可用系統(tǒng)資源萄凤,從而保證系統(tǒng)正常運(yùn)轉(zhuǎn)室抽。
談到優(yōu)先級,可能有些人會想到 Linux 進(jìn)程本身有 nice 值蛙卤,這個能決定 CPU 資源調(diào)度的優(yōu)先級狠半;而本文介紹 Android 系統(tǒng)中的 ADJ,主要決定在什么場景下什么類型的進(jìn)程可能會被殺颤难,影響的是進(jìn)程存活時間神年。ADJ 與 nice 值兩者定位不同,不過也有一定的聯(lián)系行嗤,優(yōu)先級很高的進(jìn)程已日,往往也是用戶不希望被殺的進(jìn)程,是具有有一定正相關(guān)性栅屏。
1.3 ADJ級別
ADJ級別 | 取值 | 含義 |
---|---|---|
NATIVE_ADJ |
-1000 | native進(jìn)程 |
SYSTEM_ADJ |
-900 | 僅指system_server進(jìn)程 |
PERSISTENT_PROC_ADJ |
-800 | 系統(tǒng)persistent進(jìn)程 |
PERSISTENT_SERVICE_ADJ |
-700 | 關(guān)聯(lián)著系統(tǒng)或persistent進(jìn)程 |
FOREGROUND_APP_ADJ |
0 | 前臺進(jìn)程 |
VISIBLE_APP_ADJ |
100 | 可見進(jìn)程 |
PERCEPTIBLE_APP_ADJ |
200 | 可感知進(jìn)程飘千,比如后臺音樂播放 |
BACKUP_APP_ADJ |
300 | 備份進(jìn)程 |
HEAVY_WEIGHT_APP_ADJ |
400 | 重量級進(jìn)程 |
SERVICE_ADJ |
500 | 服務(wù)進(jìn)程 |
HOME_APP_ADJ |
600 | Home進(jìn)程 |
PREVIOUS_APP_ADJ |
700 | 上一個進(jìn)程 |
SERVICE_B_ADJ |
800 | B List中的Service |
CACHED_APP_MIN_ADJ |
900 | 不可見進(jìn)程的adj最小值 |
CACHED_APP_MAX_ADJ |
906 | 不可見進(jìn)程的adj最大值 |
從 Android 7.0 開始,ADJ 采用100栈雳、200护奈、300;在這之前的版本 ADJ 采用數(shù)字 1哥纫、2霉旗、3,這樣的調(diào)整可以更進(jìn)一步地細(xì)化進(jìn)程的優(yōu)先級蛀骇,比如在 VISIBLE_APP_ADJ(100)
與 PERCEPTIBLE_APP_ADJ(200)
之間厌秒,可以有 ADJ=101、102 級別的進(jìn)程擅憔。
- 省去 lmk 對 oom_score_adj 的計算過程鸵闪,Android 7.0 之前的版本,oom_score_adj= oom_adj * 1000/17暑诸; 而Android 7.0開始蚌讼,oom_score_adj= oom_adj,不用再經(jīng)過一次轉(zhuǎn)換个榕。
1.4 LMK
為了防止剩余內(nèi)存過低啦逆,Android 在內(nèi)核空間有 lowmemorykiller (簡稱 LMK ), LMK 是通過注冊 shrinker 來觸發(fā)低內(nèi)存回收的笛洛,這個機(jī)制并不太優(yōu)雅,可能會拖慢 Shrinkers 內(nèi)存掃描速度乃坤,已從內(nèi)核 4.12 中移除苛让,后續(xù)會采用用戶空間的 LMKD + memory cgroups 機(jī)制沟蔑,這里先不展開 LMK 講解。
進(jìn)程剛啟動時 ADJ 等于 INVALID_ADJ狱杰,當(dāng)執(zhí)行完attachApplication()瘦材,該該進(jìn)程的 curAdj 和 setAdj 不相等,則會觸發(fā)執(zhí)行 setOomAdj() 將該進(jìn)程的節(jié)點(diǎn)/proc/pid
/oom_score_adj寫入oomadj值仿畸。下圖參數(shù)為 Android 原生閾值食棕,當(dāng)系統(tǒng)剩余空閑內(nèi)存低于某閾值(比如147MB),則從 ADJ 大于或等于相應(yīng)閾值(比如 900)的進(jìn)程中错沽,選擇 ADJ 值最大的進(jìn)程簿晓,如果存在多個 ADJ 相同的進(jìn)程,則選擇內(nèi)存最大的進(jìn)程千埃。 如下是 64 位機(jī)器憔儿, LMK 默認(rèn)閾值圖:
在 updateOomLevels() 過程,會根據(jù)手機(jī)屏幕尺寸或內(nèi)存大小來調(diào)整 scale放可,默認(rèn)大多數(shù)手機(jī)內(nèi)存都大于 700MB谒臼,則 scale 等于1。對于 64 位手機(jī)耀里,閾值會更大些蜈缤,具體如下。
private void updateOomLevels(int displayWidth, int displayHeight, boolean write) {
...
for (int i=0; i<mOomAdj.length; i++) {
int low = mOomMinFreeLow[i];
int high = mOomMinFreeHigh[i];
if (is64bit) {
if (i == 4) high = (high*3)/2;
else if (i == 5) high = (high*7)/4;
}
mOomMinFree[i] = (int)(low + ((high-low)*scale));
}
}
二冯挎、解讀ADJ
接下來底哥,解讀每個 ADJ 值都對應(yīng)著怎樣條件的進(jìn)程,包括正在運(yùn)行的組件以及這些組件的狀態(tài)幾何织堂。這里重點(diǎn)介紹上圖標(biāo)紅的 ADJ 級別所對應(yīng)的進(jìn)程叠艳。
Androi d系統(tǒng)中計算各進(jìn)程 ADJ 算法的核心方法:
- updateOomAdjLocked:更新 adj,當(dāng)目標(biāo)進(jìn)程為空或者被殺則返回 false易阳;否則返回 true;
- computeOomAdjLocked:計算 adj附较,返回計算后 RawAdj 值;
- applyOomAdjLocked:應(yīng)用 adj,當(dāng)需要?dú)⒌裟繕?biāo)進(jìn)程則返回 false潦俺;否則返回 true拒课。
當(dāng)Android四大組件狀態(tài)改變時會 updateOomAdjLocked() 來同步更新相應(yīng)進(jìn)程的 ADJ 優(yōu)先級。這里需要說明一下事示,當(dāng)同一個進(jìn)程有多個決定其優(yōu)先級的組件狀態(tài)時早像,取優(yōu)先級最高的 ADJ 作為最終的 ADJ。另外肖爵,進(jìn)程會通過設(shè)置 maxAdj 來限定 ADJ 的上限卢鹦。
關(guān)于分析進(jìn)程ADJ相關(guān)信息,常用命令如下:
- dumpsys meminfo劝堪,
- dumpsys activity o
- dumpsys activity p
2.0 ADJ<0的進(jìn)程
- NATIVE_ADJ(-1000):是由 init 進(jìn)程 fork 出來的 Native 進(jìn)程冀自,并不受 system 管控揉稚;
- SYSTEM_ADJ(-900):是指 system_server 進(jìn)程;
- PERSISTENT_PROC_ADJ(-800): 是指在 AndroidManifest.xml 中申明
android:persistent=”true”
的系統(tǒng)(即帶有 FLAG_SYSTEM 標(biāo)記)進(jìn)程熬粗,persistent 進(jìn)程一般情況并不會被殺搀玖,即便被殺或者發(fā)生 Crash 系統(tǒng)會立即重新拉起該進(jìn)程。 - PERSISTENT_SERVICE_ADJ(-700):是由 startIsolatedProcess() 方式啟動的進(jìn)程驻呐,或者是由system_server 或者 persistent 進(jìn)程所綁定(并且?guī)в?
BIND_ABOVE_CLIENT
或者BIND_IMPORTANT
)的服務(wù)進(jìn)程
再來說一下其他優(yōu)先級:
- BACKUP_APP_ADJ(300):執(zhí)行 bindBackupAgent() 過程的進(jìn)程
- HEAVY_WEIGHT_APP_ADJ(400): realStartActivityLocked() 過程灌诅,當(dāng)應(yīng)用的 privateFlags 標(biāo)識
PRIVATE_FLAG_CANT_SAVE_STATE
的進(jìn)程; - HOME_APP_ADJ(600):當(dāng)類型為
ACTIVITY_TYPE_HOME
的應(yīng)用含末,比如桌面 APP - PREVIOUS_APP_ADJ(700):用戶上一個使用的APP進(jìn)程
SYSTEM_ADJ(-900)
SYSTEM_ADJ: 僅指 system_server 進(jìn)程猜拾。在執(zhí)行 SystemServer 的 startBootstrapServices() 過程會調(diào)用 AMS.setSystemProcess(),將 system_server 進(jìn)程的 maxAdj 設(shè)置成 SYSTEM_ADJ
答渔,源碼如下:
public void setSystemProcess() {
...
ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(
"android", STOCK_PM_FLAGS | MATCH_SYSTEM_ONLY);
mSystemThread.installSystemApplicationInfo(info, getClass().getClassLoader());
synchronized (this) {
ProcessRecord app = newProcessRecordLocked(info, info.processName, false, 0);
app.persistent = true;
app.pid = MY_PID;
app.maxAdj = ProcessList.SYSTEM_ADJ;
app.makeActive(mSystemThread.getApplicationThread(), mProcessStats);
synchronized (mPidsSelfLocked) {
mPidsSelfLocked.put(app.pid, app);
}
updateLruProcessLocked(app, false, null);
updateOomAdjLocked();
}
...
}
PERSISTENT_PROC_ADJ(-800)
PERSISTENT_PROC_ADJ
:在 AndroidManifest.xml 中申明 android:persistent=”true” 的系統(tǒng)(即帶有FLAG_SYSTEM
標(biāo)記)進(jìn)程关带,稱之為 persistent 進(jìn)程。對于 persistent 進(jìn)程常規(guī)情況都不會被殺沼撕,一旦被殺或者發(fā)生 Crash宋雏,進(jìn)程會立即重啟。
AMS.addAppLocked()或 AMS.newProcessRecordLocked() 過程會賦值:
場景1: newProcessRecordLocked
final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess, boolean isolated, int isolatedUid) {
String proc = customProcess != null ? customProcess : info.processName;
final int userId = UserHandle.getUserId(info.uid);
int uid = info.uid;
...
final ProcessRecord r = new ProcessRecord(stats, info, proc, uid);
if (!mBooted && !mBooting
&& userId == UserHandle.USER_SYSTEM
&& (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
r.persistent = true;
r.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
}
if (isolated && isolatedUid != 0) {
r.maxAdj = ProcessList.PERSISTENT_SERVICE_ADJ;
}
return r;
}
在每一次進(jìn)程啟動的時候都會判斷該進(jìn)程是否persistent進(jìn)程务豺,如果是則會設(shè)置maxAdj=PERSISTENT_PROC_ADJ
磨总。 system_server 進(jìn)程應(yīng)該也是 persistent 進(jìn)程?
場景2:addAppLocked
final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated, String abiOverride) {
ProcessRecord app;
if (!isolated) {
app = getProcessRecordLocked(customProcess != null ? customProcess : info.processName,
info.uid, true);
} else {
app = null;
}
if (app == null) {
app = newProcessRecordLocked(info, customProcess, isolated, 0);
updateLruProcessLocked(app, false, null);
updateOomAdjLocked();
}
...
if ((info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
app.persistent = true;
app.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
}
if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
mPersistentStartingProcesses.add(app);
startProcessLocked(app, "added application",
customProcess != null ? customProcess : app.processName, abiOverride);
}
return app;
}
開機(jī)過程會先啟動persistent進(jìn)程笼沥,并賦予 maxAdj 為 PERSISTENT_PROC_ADJ
蚪燕,調(diào)用鏈:
startOtherServices()
AMS.systemReady
AMS.startPersistentApps
AMS.addAppLocked
PERSISTENT_SERVICE_ADJ(-700)
PERSISTENT_SERVICE_ADJ: startIsolatedProcess()
方式啟動的進(jìn)程,或者是由 system_server 或者 persistent 進(jìn)程所綁定的服務(wù)進(jìn)程奔浅。
場景1:newProcessRecordLocked
final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess, boolean isolated, int isolatedUid) {
String proc = customProcess != null ? customProcess : info.processName;
final int userId = UserHandle.getUserId(info.uid);
int uid = info.uid;
...
final ProcessRecord r = new ProcessRecord(stats, info, proc, uid);
if (!mBooted && !mBooting
&& userId == UserHandle.USER_SYSTEM
&& (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
r.persistent = true;
r.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
}
if (isolated && isolatedUid != 0) { //startIsolatedProcess
r.maxAdj = ProcessList.PERSISTENT_SERVICE_ADJ;
}
return r;
}
調(diào)用鏈:
startOtherServices
WebViewUpdateService.prepareWebViewInSystemServer
WebViewUpdateServiceImpl.prepareWebViewInSystemServer
WebViewUpdater.prepareWebViewInSystemServer
WebViewUpdater.onWebViewProviderChanged
SystemImpl.onWebViewProviderChanged
WebViewFactory.onWebViewProviderChanged
WebViewLibraryLoader.prepareNativeLibraries
WebViewLibraryLoader.createRelros
WebViewLibraryLoader.createRelroFile
AMS.startIsolatedProcess
BACKUP_APP_ADJ(300)
if (mBackupTarget != null && app == mBackupTarget.app) {
if (adj > ProcessList.BACKUP_APP_ADJ) {
adj = ProcessList.BACKUP_APP_ADJ;
if (procState > ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND) {
procState = ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND;
}
app.adjType = "backup";
app.cached = false;
}
if (procState > ActivityManager.PROCESS_STATE_BACKUP) {
procState = ActivityManager.PROCESS_STATE_BACKUP;
app.adjType = "backup";
}
}
- 執(zhí)行 bindBackupAgent() 過程馆纳,設(shè)置 mBackupTarget 值;
- 執(zhí)行 clearPendingBackup() 或 unbindBackupAgent() 過程汹桦,置空 mBackupTarget 值鲁驶;
HEAVY_WEIGHT_APP_ADJ(400)
- realStartActivityLocked() 過程,當(dāng)應(yīng)用的 privateFlags 標(biāo)識
PRIVATE_FLAG_CANT_SAVE_STATE
舞骆,設(shè)置 mHeavyWeightProcess 值钥弯; - finishHeavyWeightApp(), 置空 mHeavyWeightProcess 值;
HOME_APP_ADJ(600)
當(dāng)類型為 ACTIVITY_TYPE_HOME
的應(yīng)用啟動后會設(shè)置 mHomeProcess督禽,比如桌面 APP脆霎。
PREVIOUS_APP_ADJ(700)
場景1:用戶上一個使用的包含 UI 的進(jìn)程,為了給用戶在兩個 APP 之間更好的切換體驗(yàn)狈惫,將上一個進(jìn)程 ADJ 設(shè)置到 PREVIOUS_APP_ADJ
的檔次睛蛛。 當(dāng) activityStoppedLocked() 過程會更新上一個應(yīng)用。
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;
app.adjType = "previous";
}
}
場景2: 當(dāng) provider 進(jìn)程,上一次使用時間不超過 20S 的情況下玖院,優(yōu)先級不低于 PREVIOUS_APP_ADJ
菠红。provider 進(jìn)程這個是 Android 7.0 以后新增的邏輯 ,這樣做的好處是在內(nèi)存比較低的情況下避免擁有 provider 的進(jìn)程出現(xiàn)顛簸难菌,也就是啟動后殺,然后又被拉蔑滓。
if (app.lastProviderTime > 0 &&
(app.lastProviderTime+mConstants.CONTENT_PROVIDER_RETAIN_TIME) > now) {
if (adj > ProcessList.PREVIOUS_APP_ADJ) {
adj = ProcessList.PREVIOUS_APP_ADJ;
schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
app.cached = false;
app.adjType = "recent-provider";
}
if (procState > ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
procState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
app.adjType = "recent-provider";
}
}
2.1 FOREGROUND_APP_ADJ(0)
場景1:滿足以下任一條件的進(jìn)程都屬于 FOREGROUND_APP_ADJ(0)
優(yōu)先級:
- 正處于resumed狀態(tài)的Activity
- 正執(zhí)行一個生命周期回調(diào)的Service(比如執(zhí)行 onCreate , onStartCommand, onDestroy等)
- 正執(zhí)行 onReceive() 的 BroadcastReceiver
- 通過 startInstrumentation() 啟動的進(jìn)程
源碼如下:
if (PROCESS_STATE_CUR_TOP == ActivityManager.PROCESS_STATE_TOP && 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.instr != null) {
adj = ProcessList.FOREGROUND_APP_ADJ;
schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
app.adjType = "instrumentation";
procState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
} else if (isReceivingBroadcastLocked(app, mTmpBroadcastQueue)) {
adj = ProcessList.FOREGROUND_APP_ADJ;
schedGroup = (mTmpBroadcastQueue.contains(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 if (app == TOP_APP) {
adj = ProcessList.FOREGROUND_APP_ADJ;
schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
app.adjType = "top-sleeping";
foregroundActivities = true;
procState = PROCESS_STATE_CUR_TOP;
} else {
schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
adj = cachedAdj;
procState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
app.cached = true;
app.empty = true;
app.adjType = "cch-empty";
}
場景2: 當(dāng)客戶端進(jìn)程 activity 里面調(diào)用 bindService() 方法時 flags 帶有 BIND_ADJUST_WITH_ACTIVITY
參數(shù)郊酒,并且該 activity 處于可見狀態(tài),則當(dāng)前服務(wù)進(jìn)程也屬于前臺進(jìn)程键袱,源碼如下:
for (int is = app.services.size()-1; is >= 0; is--) {
ServiceRecord s = app.services.valueAt(is);
for (int conni = s.connections.size()-1; conni >= 0; conni--) {
ArrayList<ConnectionRecord> clist = s.connections.valueAt(conni);
for (int i = 0; i < clist.size(); i++) {
ConnectionRecord cr = clist.get(i);
if ((cr.flags&Context.BIND_WAIVE_PRIORITY) == 0) {
...
}
final ActivityRecord a = cr.activity;
if ((cr.flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) {
if (a != null && adj > ProcessList.FOREGROUND_APP_ADJ &&
(a.visible || a.state == ActivityState.RESUMED ||
a.state == ActivityState.PAUSING)) {
adj = ProcessList.FOREGROUND_APP_ADJ;
if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
if ((cr.flags&Context.BIND_IMPORTANT) != 0) {
schedGroup = ProcessList.SCHED_GROUP_TOP_APP_BOUND;
} else {
schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
}
}
app.cached = false;
app.adjType = "service";
app.adjTypeCode = ActivityManager.RunningAppProcessInfo
.REASON_SERVICE_IN_USE;
app.adjSource = a;
app.adjSourceProcState = procState;
app.adjTarget = s.name;
}
}
}
}
}
provider客戶端
場景3: 對于 provider 進(jìn)程燎窘,還有以下兩個條件能成為前臺進(jìn)程:
- 當(dāng) Provider 的客戶端進(jìn)程
ADJ<=FOREGROUND_APP_ADJ
時,則 Provider 進(jìn)程 ADJ 等于FOREGROUND_APP_ADJ
- 當(dāng)Provider有外部(非框架)進(jìn)程依賴蹄咖,也就是調(diào)用了 getContentProviderExternal() 方法褐健,則 ADJ 至少等于
FOREGROUND_APP_ADJ
for (int provi = app.pubProviders.size()-1; provi >= 0; provi--) {
ContentProviderRecord cpr = app.pubProviders.valueAt(provi);
//根據(jù)client來調(diào)整provider進(jìn)程的adj和procState
for (int i = cpr.connections.size()-1; i >= 0; i--) {
ContentProviderConnection conn = cpr.connections.get(i);
ProcessRecord client = conn.client;
int clientAdj = computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now);
if (adj > clientAdj) {
if (app.hasShownUi && app != mHomeProcess
&& clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
...
} else {
adj = clientAdj > ProcessList.FOREGROUND_APP_ADJ
? clientAdj : ProcessList.FOREGROUND_APP_ADJ;
adjType = "provider";
}
app.cached &= client.cached;
}
...
}
//根據(jù)provider外部依賴情況來調(diào)整adj和schedGroup
if (cpr.hasExternalProcessHandles()) {
if (adj > ProcessList.FOREGROUND_APP_ADJ) {
adj = ProcessList.FOREGROUND_APP_ADJ;
schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
app.cached = false;
app.adjType = "ext-provider";
app.adjTarget = cpr.name;
}
}
}
2.2 VISIBLE_APP_ADJ(100)
可見進(jìn)程:當(dāng) ActivityRecord 的 visible=true,也就是 Activity 可見的進(jìn)程澜汤。
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.visible) {
if (adj > ProcessList.VISIBLE_APP_ADJ) {
adj = ProcessList.VISIBLE_APP_ADJ;
app.adjType = "vis-activity";
}
if (procState > PROCESS_STATE_CUR_TOP) {
procState = PROCESS_STATE_CUR_TOP;
app.adjType = "vis-activity";
}
schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
app.cached = false;
app.empty = false;
foregroundActivities = true;
final TaskRecord task = r.getTask();
if (task != null && minLayer > 0) {
final int layer = task.mLayerRank;
if (layer >= 0 && minLayer > layer) {
minLayer = layer;
}
}
break;
}
...
}
if (adj == ProcessList.VISIBLE_APP_ADJ) {
adj += minLayer;
}
}
從 Android P 開始蚜迅,進(jìn)一步細(xì)化 ADJ 級別,增加了 VISIBLE_APP_LAYER_MAX(99)
俊抵,是指VISIBLE_APP_ADJ(100)跟PERCEPTIBLE_APP_ADJ(200)
之間有 99 個槽谁不,則可見級別 ADJ 的取值范圍為 [100,199]。 算法會根據(jù)其所在 task 的 mLayerRank 來調(diào)整其 ADJ徽诲,100 加上 mLayerRank 就等于目標(biāo) ADJ刹帕,layer 越大,則 ADJ 越小谎替。
關(guān)于 TaskRecord 的 mLayerRank 的計算方式是在 updateOomAdjLocked() 過程調(diào)用 ASS 的 rankTaskLayersIfNeeded() 方法偷溺,如下:
[-> ActivityStackSupervisor.java]
void rankTaskLayersIfNeeded() {
if (!mTaskLayersChanged) {
return;
}
mTaskLayersChanged = false;
for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); displayNdx++) {
final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
int baseLayer = 0;
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
baseLayer += stack.rankTaskLayers(baseLayer);
}
}
}
[-> ActivityStack.java]
final int rankTaskLayers(int baseLayer) {
int layer = 0;
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
ActivityRecord r = task.topRunningActivityLocked();
if (r == null || r.finishing || !r.visible) {
task.mLayerRank = -1;
} else {
task.mLayerRank = baseLayer + layer++;
}
}
return layer;
}
當(dāng) TaskRecord 頂部的 ActivityRecord 為空或者結(jié)束或者不可見時,則設(shè)置該 TaskRecord 的 mLayerRank 等于 -1; 每個 ActivityDisplay 的 baseLayer 都是從 0 開始钱贯,從最上面的 TaskRecord 開始挫掏,第一個 ADJ=100,從上至下依次加 1喷舀,直到 199 為上限砍濒。
service客戶端
ServiceRecord 的成員變量 startRequested=true,是指被顯式調(diào)用了 startService() 方法硫麻。當(dāng) service 被 stop 或 kill 會將其置為 false爸邢。
一般情況下,即便客戶端進(jìn)程處于前臺進(jìn)程 (ADJ=0) 級別拿愧,服務(wù)進(jìn)程只會提升到可見 (ADJ=1) 級別杠河。以下 flags 是由調(diào)用 bindService() 過程所傳遞的 flags 來決定的。
flag | 含義 |
---|---|
BIND_WAIVE_PRIORITY | 是指客戶端進(jìn)程的優(yōu)先級不會影響目標(biāo)服務(wù)進(jìn)程的優(yōu)先級。比如當(dāng)調(diào)用bindService又不希望提升目標(biāo)服務(wù)進(jìn)程的優(yōu)先級的情況下券敌,可以使用該flags |
BIND_ADJUST_WITH_ACTIVITY | 是指當(dāng)從Activity綁定到該進(jìn)程時唾戚,允許目標(biāo)服務(wù)進(jìn)程根據(jù)該activity的可見性來提升優(yōu)先級 |
BIND_ABOVE_CLIENT | 當(dāng)客戶端進(jìn)程綁定到一個服務(wù)進(jìn)程時,則服務(wù)進(jìn)程比客戶端進(jìn)程更重要 |
BIND_IMPORTANT | 標(biāo)記該服務(wù)對于客戶端進(jìn)程很重要待诅,當(dāng)客戶端進(jìn)程處于前臺進(jìn)程(ADJ=0)級別時叹坦,會把服務(wù)進(jìn)程也提升到前臺進(jìn)程級別 |
BIND_NOT_VISIBLE | 當(dāng)客戶端進(jìn)程處于可見(ADJ=1)級別,也不允許被綁定的服務(wù)進(jìn)程提升到可見級別卑雁,該類服務(wù)進(jìn)程的優(yōu)先級上限為可感知(ADJ=2)級別 |
BIND_NOT_FOREGROUND | 不允許被綁定的服務(wù)進(jìn)程提升到前臺調(diào)度優(yōu)先級募书,但是內(nèi)存優(yōu)先級可以提升到前臺級別。比如不希望服務(wù)進(jìn)程占用 |
作為工程師很多時候可能還是想看看源碼 测蹲。但是關(guān)于 ADJ 計算這一塊源碼場景 computeOomAdjLocked() 莹捡, 為了更清晰地說明客戶端進(jìn)程如何影響服務(wù)進(jìn)程,在保證不失去原意的情況下重寫了這塊部分邏輯:
這個過程主要根據(jù) service 本身扣甲、client 端情況以及 activity 狀態(tài)分別來調(diào)整 adj 和 schedGroup
for (int is = app.services.size()-1; is >= 0; is--) {
ServiceRecord s = app.services.valueAt(is);
if (s.startRequested) {
... // 根據(jù)service本身調(diào)整adj和adjType
}
for (int conni = s.connections.size()-1; conni >= 0; conni--) {
ArrayList<ConnectionRecord> clist = s.connections.valueAt(conni);
for (int i = 0; i < clist.size(); i++) {
ConnectionRecord cr = clist.get(i);
//根據(jù)client端來調(diào)整adj
if ((cr.flags&Context.BIND_WAIVE_PRIORITY) == 0) {
if (adj > clientAdj) {
if (app.hasShownUi && app != mHomeProcess
&& clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
...
} else {
int newAdj = clientAdj;
if ((cr.flags&(Context.BIND_ABOVE_CLIENT
|Context.BIND_IMPORTANT)) != 0) {
if(clientAdj < ProcessList.PERSISTENT_SERVICE_ADJ) {
newAdj = PERSISTENT_SERVICE_ADJ;
}
} else if ((cr.flags&Context.BIND_NOT_VISIBLE) != 0) {
if(clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ) {
newAdj = PERCEPTIBLE_APP_ADJ;
}
} else {
if (clientAdj < ProcessList.VISIBLE_APP_ADJ) {
newAdj = VISIBLE_APP_ADJ;
}
}
if (adj > newAdj) {
adj = newAdj;
adjType = "service";
}
}
}
}
final ActivityRecord a = cr.activity;
// 根據(jù)client的activity來調(diào)整adj和schedGroup
if ((cr.flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) {
...
}
}
}
}
上段代碼說明服務(wù)端進(jìn)程優(yōu)先級(adj)不會低于客戶端進(jìn)程優(yōu)先級(newAdj)篮赢,而newAdj的上限受限于flags,具體服務(wù)端進(jìn)程受客戶端進(jìn)程影響的ADJ上限如下:
-
BIND_ABOVE_CLIENT
或BIND_IMPORTANT
的情況下琉挖,ADJ 上限為PERSISTENT_SERVICE_ADJ
启泣; -
BIND_NOT_VISIBLE
的情況下, ADJ 上限為PERCEPTIBLE_APP_ADJ
粹排; - 否則种远,一般情況下,ADJ 上限為
VISIBLE_APP_ADJ
顽耳;
由此坠敷,可見當(dāng) bindService 過程帶有 BIND_ABOVE_CLIENT
或者 BIND_IMPORTANT
flags 的同時,客戶端進(jìn)程 ADJ 小于或等于 PERSISTENT_SERVICE_ADJ
的情況下射富,該進(jìn)程則為 PERSISTENT_SERVICE_ADJ
膝迎。另外,即便是啟動過 Activity 的進(jìn)程胰耗,當(dāng)客戶端進(jìn)程 ADJ<=200 時限次,還是可以提升該服務(wù)進(jìn)程的優(yōu)先級。
2.3 PERCEPTIBLE_APP_ADJ(200)
可感知進(jìn)程:當(dāng)該進(jìn)程存在不可見的 Activity柴灯,但 Activity 正處于 PAUSING卖漫、PAUSED、STOPPING 狀態(tài)赠群,則為 PERCEPTIBLE_APP_ADJ
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.visible) {
...
} else if (r.state == ActivityState.PAUSING || r.state == ActivityState.PAUSED) {
if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
adj = ProcessList.PERCEPTIBLE_APP_ADJ;
app.adjType = "pause-activity";
}
if (procState > PROCESS_STATE_CUR_TOP) {
procState = PROCESS_STATE_CUR_TOP;
app.adjType = "pause-activity";
}
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 = "stop-activity";
}
if (!r.finishing) {
if (procState > ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
procState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
app.adjType = "stop-activity";
}
}
app.cached = false;
app.empty = false;
foregroundActivities = true;
}
}
}
滿足以下任一條件的進(jìn)程也屬于可感知進(jìn)程:
- foregroundServices 非空:前臺服務(wù)進(jìn)程羊始,執(zhí)行 startForegroundService() 方法
- app.forcingToImportant 非空:執(zhí)行setProcessImportant()方法,比如 Toast 彈出過程查描。
- hasOverlayUi 非空:非 activity 的 UI 位于屏幕最頂層突委,比如顯示類型
TYPE_APPLICATION_OVERLAY
的窗口