生還是死? 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ā)生什么:
- START_STICKY 表示你希望系統(tǒng)可用的時候自動重啟你的服務(wù),但你不關(guān)心是否能獲得最后一次的 Intent (例如,你可以重建自己的狀態(tài)或者控制自己的 start/stop 生命周期)。
- START_REDELIVER_INTENT 是為那些在被殺死之后重啟時重新獲得 Intent 的服務(wù)的,直到你用傳遞給 onStartCommand() 方法的 startId 參數(shù)調(diào)用 stopSelf() 為止。這里你會使用 Intent 和 startId 作為隊(duì)列完成工作。
- 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ā)生!