生還是死? Android 進(jìn)程優(yōu)先級詳解

生還是死? Android 進(jìn)程優(yōu)先級詳解

英文原址:Who lives and who dies? Process priorities on Android

讓我們面對現(xiàn)實(shí):移動設(shè)備上沒有無限的內(nèi)存、無限的電池或者其它無限的資源。這對應(yīng)用而言意味著你應(yīng)該把進(jìn)程死亡作為應(yīng)用生命周期的一個自然過程對待。最重要的是確保殺死進(jìn)程及內(nèi)存回收不會對用戶造成負(fù)面影響。事實(shí)上,Android 中的多數(shù)進(jìn)程架構(gòu)都是為了確保特定的順序而特別設(shè)計(jì)的,并按重要性層次遵循一組模式。

Android 進(jìn)程層次

你會發(fā)現(xiàn)最重要的進(jìn)程被稱為前臺進(jìn)程,然后依次是任何可見進(jìn)程、服務(wù)進(jìn)程、后臺進(jìn)程,最后是空進(jìn)程。這個文檔中有詳細(xì)描述,這里我們將進(jìn)一步展開。

注意,當(dāng)我們談?wù)撎囟ńM件(服務(wù)、activity)時,Android 只殺死進(jìn)程,而不是組件。當(dāng)然,這不會阻止通常的垃圾回收進(jìn)程(它要回收沒有任何引用的對象的內(nèi)存),不過這是另一個主題了。

前臺進(jìn)程

你會想正在與用戶交互的東西是最重要的需要保證活著的,這應(yīng)該完全正確。但是“正在與用戶交互”這個定義有點(diǎn)模糊。當(dāng)前的前臺 Activity 毫無爭議屬于這一類,它是已經(jīng)調(diào)用了 onResume() 方法但還沒有收到 onPause() 調(diào)用的 Activity .

一些 activity 在依靠他們自己的同時,也可能依賴 bound service。任何進(jìn)程,如果它持有一個綁定到前臺 activity 的服務(wù),那么它也被賦予了同樣的前臺優(yōu)先級。這完全符合直覺,如果前臺 activity 認(rèn)為和那個服務(wù)保持持久連接很重要,那么保持這個服務(wù)活著就對 activity 和 Android 很重要。對于正在與前臺服務(wù)交互的 content provider 也是如此。

但是誰說用戶能察覺到的只有 activity ?如果正在播放的音樂突然停止或?qū)Ш椒较蛲蝗幌В乙欢〞軔阑稹P液茫珹ndroid 可以讓服務(wù)使用 startForeground() 方法成為高優(yōu)先級前臺服務(wù)。這絕對是媒體播放的最佳實(shí)踐,但是這里要問一個重要問題“如果服務(wù)停止了,用戶會立刻察覺到嗎?”。前臺服務(wù)應(yīng)該僅被用于關(guān)鍵的、可被立刻察覺的場景。

注意:要成為前臺服務(wù)需要在服務(wù)中包含一個通知以便讓用戶注意到這個服務(wù)正在運(yùn)行。如果你覺得你的使用場景不需要這個通知,那么前臺服務(wù)對你可能不是正確的選擇(是的,成為前臺服務(wù)并不要求一定運(yùn)行在后臺,見下文)。

可見進(jìn)程

等下,我想我已經(jīng)談到了當(dāng)前的 activity?你會發(fā)現(xiàn) activity 可見的時候不一定在前臺。一個簡單的例子是前臺的 activity 使用對話框啟動了一個新的 activity 或者一個透明 activity 。另一個例子是當(dāng)你調(diào)用運(yùn)行時權(quán)限對話框時(事實(shí)上它就是一個 activity!)。

在收到 onStart() 和收到 onStop() 方法期間的 activity 是可見 activity 。在這兩個方法調(diào)用之間,你可以做所有可見 activity 能做的事情(實(shí)時更新屏幕等)。

和前臺 activity 類似,可見 activity 的 bound service 和 content provider 也處于可見進(jìn)程狀態(tài)。這同樣是為了保證使用中的 activity 所依賴的進(jìn)程不會被過早地殺掉。

但請記住,只是可見并不意味著不能被殺掉。如果來自前臺進(jìn)程的內(nèi)存壓力過大,可見進(jìn)程仍有可能被殺掉。從用戶的角度看,這意味著當(dāng)前 activity 背后的可見 activity 會被黑屏代替。當(dāng)然,如果你正確地重建你的 activity ,在前臺 activity 關(guān)閉之后你的進(jìn)程和 activity 會立刻恢復(fù)而沒有數(shù)據(jù)損失。

注意:你的 activity 和進(jìn)程即使可見也可能被殺掉是因?yàn)? startActivityForResult()+onActivityResult() 或 requestPermissions()+onRequestPermissionsResult() 流程沒有獲得回調(diào)類的實(shí)例。如果你的整個進(jìn)程死了,那么所有的回調(diào)類實(shí)例也死了。如果你看到使用回調(diào)方式的庫,你應(yīng)該意識到這在低內(nèi)存壓力情況下無法完成。

服務(wù)進(jìn)程

如果你的進(jìn)程不屬于以上兩種類別,而你有一個啟動的服務(wù)(started service),那么它被看作是一個服務(wù)進(jìn)程。對于許多在后臺做處理(如加載數(shù)據(jù))而沒有立即成為前臺服務(wù)的應(yīng)用都屬于這種情況。

這沒有問題!絕大多數(shù)情況,這是后臺處理的最佳方式。這種進(jìn)程只有在前面講的可見進(jìn)程和前臺進(jìn)程做了太多事情需要更多資源的時候才會被殺掉。

請?zhí)貏e注意從 onStartCommand() 返回的常量,如果你的服務(wù)由于內(nèi)存壓力被殺掉,它表示控制什么發(fā)生什么:

  1. START_STICKY 表示你希望系統(tǒng)可用的時候自動重啟你的服務(wù),但你不關(guān)心是否能獲得最后一次的 Intent (例如,你可以重建自己的狀態(tài)或者控制自己的 start/stop 生命周期)。
  1. START_REDELIVER_INTENT 是為那些在被殺死之后重啟時重新獲得 Intent 的服務(wù)的,直到你用傳遞給 onStartCommand() 方法的 startId 參數(shù)調(diào)用 stopSelf() 為止。這里你會使用 Intent 和 startId 作為隊(duì)列完成工作。
  1. START_NOT_STICKY 用于那些殺掉也沒關(guān)系的服務(wù)。這適合那些管理周期性任務(wù)的服務(wù),它們只是等待下一個時間窗口工作。

后臺進(jìn)程

比如說你的 Activity 一開始是前臺 Activity,但是用戶點(diǎn)了 home 鍵導(dǎo)致 onStop() 方法被調(diào)用。假設(shè)你之前一直是高優(yōu)先級進(jìn)程類別,這時你的進(jìn)程將變?yōu)楹笈_進(jìn)程類別。在一般操作場景下,設(shè)備上的許多內(nèi)存就是用在這上面的,讓你重新回到之前打開過的某個 activity 。

Android 不是為了殺而殺的(記住:從頭啟動是有代價的),所以這些進(jìn)程會保留一段時間,直到更高優(yōu)先級進(jìn)程需要內(nèi)存的時候才被回收,并且是按照最近最少使用順序(最老的會被優(yōu)先回收)。然而,當(dāng)他們被殺掉的時候和可見 activity 處理情況一樣,你應(yīng)該能夠在不丟失用戶狀態(tài)的情況下重建這些 activity 。

空進(jìn)程

在任何層次中,空進(jìn)程都是最低優(yōu)先級的。如果不屬于以上類別,那它就是這種。這里沒有活躍的組件,只是出于緩存的目的而被保留(為了更加有效地使用內(nèi)存而不是完全釋放掉),只要 Android 需要可以隨時殺掉它們。

注意事項(xiàng)

當(dāng)我們談?wù)撨M(jìn)程優(yōu)先級的時候是以 activity、service 這樣的組件來說的,但請記住這些優(yōu)先級是在進(jìn)程的級別上,不是組件級別上。只要一個組件(比如一個前臺服務(wù))就會將整個進(jìn)程變?yōu)榍芭_進(jìn)程。絕大多數(shù)應(yīng)用是單進(jìn)程的,如果你有生命周期差異很大的不同部分或者某個部分非常重量型,那么強(qiáng)烈建議你把它們分為不同的進(jìn)程,讓重量級進(jìn)程盡早被回收。

同樣重要的是,你的進(jìn)程屬于什么類別是組件層面發(fā)生的事情決定的。這意味著把非常重要的長時間運(yùn)行的操作放在 activity 所在進(jìn)程的一個獨(dú)立線程中的做法,在進(jìn)程突然變成后臺進(jìn)程的時候可能會遇到問題。使用你能用到的工具(一個服務(wù)或基于優(yōu)先級的前臺服務(wù))來確保系統(tǒng)知道你在做什么。

整個系統(tǒng)這樣工作都是為了用戶。做個好公民,做好你的應(yīng)用,始終讓自己工作在合適的優(yōu)先級上。請記住,作為一個開發(fā)者,你使用的手機(jī)可能比你用戶的最差手機(jī)快得多得多,你可能從來不會看到可見進(jìn)程被殺死,遠(yuǎn)少于服務(wù)進(jìn)程,但是這不意味著它不會發(fā)生!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,362評論 6 537
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,013評論 3 423
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,346評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,421評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,146評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,534評論 1 325
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,585評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,767評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,318評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,074評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,258評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,828評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,486評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,916評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,156評論 1 290
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,993評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,234評論 2 375

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