Android平臺(tái)App進(jìn)程優(yōu)先級(jí)

我們都知道Android手機(jī)上是可以安裝很多App的垂睬,每一個(gè)App至少是會(huì)有一個(gè)進(jìn)程的。創(chuàng)建進(jìn)程是件麻煩而且耗資源的事情抗悍,Android為了讓App啟動(dòng)的時(shí)候能更快驹饺,會(huì)把那么暫時(shí)不使用的App的進(jìn)程緩存起來(lái),但是內(nèi)存是有限的啊缴渊,總不能讓所有的進(jìn)程都放在內(nèi)存里邊吧赏壹,所以Android有一個(gè)淘汰機(jī)制,會(huì)根據(jù)App的運(yùn)行狀態(tài)設(shè)置一個(gè)進(jìn)程的優(yōu)先級(jí)(oom_adj)衔沼,然后根據(jù)內(nèi)存的緊張程度蝌借,把那些優(yōu)先級(jí)低(oom_adj值大)的進(jìn)程kill掉,以保證其他的進(jìn)程能夠有足夠的內(nèi)存使用指蚁。

進(jìn)程

  1. Zygote進(jìn)程
    這個(gè)是Android框架的主要進(jìn)程菩佑,所有的App進(jìn)程以及系統(tǒng)服務(wù)進(jìn)程SystemServer都是由Zygote進(jìn)程Fork出來(lái)的
  2. App的主進(jìn)程
    每一個(gè)App的運(yùn)行都是在一個(gè)獨(dú)立的進(jìn)程,進(jìn)程的名字就是App的packagename欣舵,這些進(jìn)程都是從Zygote進(jìn)程Fork出來(lái)的擎鸠,并受AMS(ActivityManagerService)管理
  3. App的輔助進(jìn)程
    可以允許App有多個(gè)進(jìn)程,在AndroidManifest.xml里邊配置android:process屬性缘圈,就可以開(kāi)啟多進(jìn)程劣光,這些進(jìn)程名字都是packagename:name這種袜蚕,以區(qū)分是屬于哪個(gè)App,我一般稱之為輔助進(jìn)程绢涡。但這些進(jìn)程也都跟主進(jìn)程一樣牲剃,也是從Zygote進(jìn)程Fork出來(lái)的,并受AMS管理
  4. Native進(jìn)程
    Android除了使用Java雄可,還有NDK凿傅,可以使用C/C++去開(kāi)發(fā),然后這里也是可以Fork出進(jìn)程的数苫,我一般稱之為Native進(jìn)程聪舒,Native進(jìn)程可以不受AMS管理,自由度很大虐急,本文暫且不講

優(yōu)先級(jí)

App進(jìn)程的優(yōu)先級(jí)是在com.android.server.am.ProcessList類(lèi)里邊定義的箱残,這個(gè)類(lèi)在Android的API里邊是找不到的,要想看看里邊的實(shí)現(xiàn)可以去Android SDK里邊去找止吁,位于${android-sdk-path}\sources\android-23\com\android\server\am\ProcessList.java

主要有這么幾個(gè)優(yōu)先級(jí)(oom_adj值):

  • UNKNOWN_ADJ = 16
    預(yù)留的最低級(jí)別被辑,一般對(duì)于緩存的進(jìn)程才有可能設(shè)置成這個(gè)級(jí)別

Adjustment used in certain places where we don't know it yet. (Generally this is something that is going to be cached, but we don't know the exact value in the cached range to assign yet.)

  • CACHED_APP_MAX_ADJ = 15
    緩存進(jìn)程,空進(jìn)程敬惦,在內(nèi)存不足的情況下就會(huì)優(yōu)先被kill

This is a process only hosting activities that are not visible, so it can be killed without any disruption.

  • CACHED_APP_MIN_ADJ = 9
    緩存進(jìn)程盼理,也就是空進(jìn)程
  • SERVICE_B_ADJ = 8
    不活躍的進(jìn)程

The B list of SERVICE_ADJ -- these are the old and decrepit services that aren't as shiny and interesting as the ones in the A list.

  • PREVIOUS_APP_ADJ = 7
    切換進(jìn)程

This is the process of the previous application that the user was in. This process is kept above other things, because it is very common to switch back to the previous app. This is important both for recent task switch (toggling between the two top recent apps) as well as normal UI flow such as clicking on a URI in the e-mail app to view in the browser, and then pressing back to return to e-mail.

  • HOME_APP_ADJ = 6
    與Home交互的進(jìn)程

This is a process holding the home application -- we want to try avoiding killing it, even if it would normally be in the background, because the user interacts with it so much.

  • SERVICE_ADJ = 5
    有Service的進(jìn)程

This is a process holding an application service -- killing it will not have much of an impact as far as the user is concerned.

  • HEAVY_WEIGHT_APP_ADJ = 4
    高權(quán)重進(jìn)程

This is a process with a heavy-weight application. It is in the background, but we want to try to avoid killing it. Value set in system/rootdir/init.rc on startup.

  • BACKUP_APP_ADJ = 3
    正在備份的進(jìn)程

This is a process currently hosting a backup operation. Killing it is not entirely fatal but is generally a bad idea.

  • PERCEPTIBLE_APP_ADJ = 2
    可感知的進(jìn)程,比如那種播放音樂(lè)

This is a process only hosting components that are perceptible to the user, and we really want to avoid killing them, but they are not immediately visible. An example is background music playback.

  • VISIBLE_APP_ADJ = 1
    可見(jiàn)進(jìn)程

This is a process only hosting activities that are visible to the user, so we'd prefer they don't disappear.

  • FOREGROUND_APP_ADJ = 0
    前臺(tái)進(jìn)程

This is the process running the current foreground app. We'd really rather not kill it!

  • PERSISTENT_SERVICE_ADJ = -11
    重要進(jìn)程

This is a process that the system or a persistent process has bound to, and indicated it is important.

  • PERSISTENT_PROC_ADJ = -12
    核心進(jìn)程

This is a system persistent process, such as telephony. Definitely don't want to kill it, but doing so is not completely fatal.

  • SYSTEM_ADJ = -16
    系統(tǒng)進(jìn)程

The system process runs at the default adjustment.

  • NATIVE_ADJ = -17
    系統(tǒng)起的Native進(jìn)程

Special code for native processes that are not being managed by the system (so don't have an oom adj assigned by the system).

在Android-18及以下空進(jìn)程不叫CACHED_APP_MIN_ADJ 俄删,叫HIDDEN_APP_MIN_ADJ宏怔,有這么點(diǎn)不一樣,但是值都一樣畴椰。

如何查看App進(jìn)程優(yōu)先級(jí)

對(duì)于App進(jìn)程举哟,因?yàn)槭蹵MS管理,都會(huì)有一個(gè)oom_adj的文件記錄著oom_adj的值迅矛,但是對(duì)于Native進(jìn)程妨猩,那AMS就管不著了。

//oom_adj的值就是進(jìn)程的優(yōu)先級(jí)秽褒,
//查看oom_adj值
cat /proc/${pid}/oom_adj

lowmemorykiller 機(jī)制

這個(gè)機(jī)制是在goldfish層面實(shí)現(xiàn)的壶硅,如果要想具體了解是怎么選擇殺進(jìn)程的話,需要去看Android drivers下的lowmemorykiller.c的代碼销斟。

編譯過(guò)goldfish的人庐椒,應(yīng)該知道它其實(shí)就是Linux kernel。因?yàn)锳ndroid是基于Linux的操作系統(tǒng)嘛蚂踊,kernel其實(shí)也是跟著Linux kernel走的约谈,只不過(guò)在里邊需要開(kāi)發(fā)(或者精簡(jiǎn))一些專(zhuān)門(mén)針對(duì)Android的東西,lowmemorykiller 就是其中的一部分,是個(gè)驅(qū)動(dòng)棱诱。至于為啥會(huì)被放在drivers/staging目錄下泼橘,可以參考下這篇文章《小議Linux staging tree》

有這么幾個(gè)關(guān)鍵點(diǎn):

  1. 向系統(tǒng)注冊(cè)lowmem_shrinker回調(diào),當(dāng)系統(tǒng)空閑內(nèi)存不足時(shí)調(diào)用迈勋,去殺進(jìn)程
  2. 把空閑內(nèi)存低于多少炬灭,就去殺那個(gè)級(jí)別的進(jìn)程,我稱之為策略
  3. 殺的策略由application層根據(jù)內(nèi)存狀況指定,寫(xiě)入文件傳遞給驅(qū)動(dòng)
  4. 根據(jù)策略會(huì)計(jì)算出min_score_adj
  5. 每個(gè)App進(jìn)程都有oom_adj值,根據(jù)oom_adj值計(jì)算出oom_score_adj(得分值)鹏控,oom_adj越大,oom_score_adj越高鼻吮,高于min_score_adj的被列入死亡名單
  6. 在死亡名單里面選一個(gè)占內(nèi)存最大的進(jìn)程kill掉

所以當(dāng)內(nèi)存不足的時(shí)候,進(jìn)程優(yōu)先級(jí)低的(oom_adj越大的)较鼓,占內(nèi)存大的App進(jìn)程將會(huì)被優(yōu)先kill掉狈网。

至于這個(gè)策略嘛,各個(gè)ROM可能不一致的笨腥,根據(jù)內(nèi)存的大小,系統(tǒng)的嚴(yán)格程度來(lái)制定勇垛,但都只能分6個(gè)級(jí)別脖母。


查看傳給lowmemorykiller驅(qū)動(dòng)的策略文件

minfree:列舉6個(gè)內(nèi)存閥值。
比如:32768,61440,73728,129024,147456,184320
adj:列舉6個(gè)oom_adj的級(jí)別闲孤。
比如:0,1,2,3,9,15
這兩個(gè)值相互對(duì)應(yīng)谆级,空閑內(nèi)存低于多少的時(shí)候就kill那個(gè)級(jí)別的進(jìn)程。

ProcessList.java里邊定義的策略參數(shù)

當(dāng)然有些ROM也會(huì)把這個(gè)參數(shù)定義在init.rc里邊讼积,開(kāi)機(jī)后再寫(xiě)入minfree和adj文件肥照,傳遞給驅(qū)動(dòng)

trimApplications機(jī)制

僅僅只有低內(nèi)存的情況下才去kill進(jìn)程嗎?很明顯不是勤众,對(duì)于App的運(yùn)行是有很多狀況的舆绎,比如Crash、App退出们颜、系統(tǒng)清理吕朵、卸載。

trimApplications是一個(gè)方法窥突,定義在ActivityManagerService里邊努溃,Android API里邊也是沒(méi)有這個(gè)類(lèi)的,得去SDK里邊找阻问,位于${android-sdk-path}\sources\android-23\com\android\server\am\ActivityManagerService.java

這個(gè)機(jī)制大概也有幾個(gè)關(guān)鍵點(diǎn):

  1. package已被卸載的無(wú)用進(jìn)程會(huì)被Kill
  2. persistent的App會(huì)被優(yōu)先照顧梧税,進(jìn)程優(yōu)先級(jí)設(shè)置為PERSISTENT_PROC_ADJ=-12
  3. Activity僅在主進(jìn)程跑的App會(huì)被認(rèn)為是高權(quán)重進(jìn)程,HEAVY_WEIGHT_APP_ADJ=4
  4. 只有前臺(tái)進(jìn)程才是FOREGROUND_APP_ADJ=0,前臺(tái)進(jìn)程不會(huì)被殺
  5. 每當(dāng)Activity第队、Service等生命周期發(fā)生變化的時(shí)候都會(huì)引起進(jìn)程的oo_adj的調(diào)整
  6. 進(jìn)程里邊沒(méi)有任何的Activity的存在優(yōu)先被殺
  7. 空進(jìn)程最容易被殺

如何提高后臺(tái)App進(jìn)程的優(yōu)先級(jí)

其實(shí)Android框架的思想是很好的哮塞,對(duì)于空的進(jìn)程,沒(méi)事干的進(jìn)程直接kill掉斥铺,對(duì)于用戶體驗(yàn)來(lái)講是不會(huì)有影響的彻桃,但是往往我們的App都會(huì)有推送這個(gè)功能,恰巧GCM(Google Cloud Messaging)在國(guó)內(nèi)又不能用晾蜘,所以很多情況下我們也會(huì)希望App在后臺(tái)的時(shí)候也盡量不要被殺邻眷。

可以考慮把后臺(tái)App進(jìn)程的優(yōu)先級(jí)提高,下面有幾個(gè)方法:

  • AndroidManifest.xml中配置persistent屬性
<application android:name="App"
  android:persistent="true"
  android:label="@string/dialerIconLabel"
  android:icon="@drawable/ic_launcher_phone">
  • 重載back按鍵事件剔交,讓activity在后臺(tái)運(yùn)行肆饶,不要Destory
  • 開(kāi)Service,并設(shè)置前臺(tái)運(yùn)行方式
  • 與NotificationManager交互岖常,讓進(jìn)程變成可感知進(jìn)程
  • 發(fā)送/接收廣播驯镊,別讓自己變成空進(jìn)程

很明顯這會(huì)消耗更多的電量,也有點(diǎn)流氓竭鞍,有些需求也許本就不該做板惑。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市偎快,隨后出現(xiàn)的幾起案子冯乘,更是在濱河造成了極大的恐慌,老刑警劉巖晒夹,帶你破解...
    沈念sama閱讀 216,744評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件裆馒,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡丐怯,警方通過(guò)查閱死者的電腦和手機(jī)喷好,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)读跷,“玉大人梗搅,你說(shuō)我怎么就攤上這事⌒Ю溃” “怎么了些膨?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,105評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)钦铺。 經(jīng)常有香客問(wèn)我订雾,道長(zhǎng),這世上最難降的妖魔是什么矛洞? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,242評(píng)論 1 292
  • 正文 為了忘掉前任洼哎,我火速辦了婚禮烫映,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘噩峦。我一直安慰自己锭沟,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,269評(píng)論 6 389
  • 文/花漫 我一把揭開(kāi)白布识补。 她就那樣靜靜地躺著族淮,像睡著了一般。 火紅的嫁衣襯著肌膚如雪凭涂。 梳的紋絲不亂的頭發(fā)上祝辣,一...
    開(kāi)封第一講書(shū)人閱讀 51,215評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音切油,去河邊找鬼蝙斜。 笑死,一個(gè)胖子當(dāng)著我的面吹牛澎胡,可吹牛的內(nèi)容都是我干的孕荠。 我是一名探鬼主播,決...
    沈念sama閱讀 40,096評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼攻谁,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼稚伍!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起戚宦,我...
    開(kāi)封第一講書(shū)人閱讀 38,939評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤个曙,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后阁苞,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,354評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡祠挫,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,573評(píng)論 2 333
  • 正文 我和宋清朗相戀三年那槽,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片等舔。...
    茶點(diǎn)故事閱讀 39,745評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡骚灸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出慌植,到底是詐尸還是另有隱情甚牲,我是刑警寧澤,帶...
    沈念sama閱讀 35,448評(píng)論 5 344
  • 正文 年R本政府宣布蝶柿,位于F島的核電站丈钙,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏交汤。R本人自食惡果不足惜雏赦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,048評(píng)論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧星岗,春花似錦填大、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,683評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至寥掐,卻和暖如春靴寂,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背曹仗。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,838評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工榨汤, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人怎茫。 一個(gè)月前我還...
    沈念sama閱讀 47,776評(píng)論 2 369
  • 正文 我出身青樓收壕,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親轨蛤。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蜜宪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,652評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容