讓咱們面對現實:移動設備上沒有無限的內存、無限的電池或者其它無限的資源。這對應用而言意味着 你應該把進程死亡做爲應用生命週期的一個天然過程對待。最重要的是確保殺死進程及內存回收不會對用戶形成負面影響。事實上,Android 中的多數進程架構都是爲了確保特定的順序而特別設計的,並按重要性層次遵循一組模式。
你會發現最重要的進程被稱爲前臺進程,而後依次是任何可見進程、服務進程、後臺進程,最後是空進程。這個文檔中有詳細描述,這裏咱們將進一步展開。面試
注意,當咱們談論特定組件(服務、activity)時,Android 只殺死進程,而不是組件。固然,這不會阻止一般的垃圾回收進程(它要回收沒有任何引用的對象的內存),不過這是另外一個主題了。shell
你會想正在與用戶交互的東西是最重要的須要保證活着的,這應該徹底正確。可是「正在與用戶交互」這個定義有點模糊。當前的前臺 Activity 毫無爭議屬於這一類,它是已經調用了 onResume() 方法但尚未收到 onPause() 調用的 Activity 。緩存
一些 activity 在依靠他們本身的同時,也可能依賴 bound service 。任何進程,若是它持有一個綁定到前臺 activity 的服務,那麼它也被賦予了一樣的前臺優先級。這徹底符合直覺,若是前臺 activity 認爲和那個服務保持持久鏈接很重要,那麼保持這個服務活着就對 activity 和 Android 很重要。對於正在與前臺服務交互的 content provider 也是如此。架構
可是誰說用戶能察覺到的只有 activity ?若是正在播放的音樂忽然中止或導航方向忽然消失,我必定會很惱火。幸虧,Android 可讓服務使用 startForeground() 方法成爲高優先級前臺服務。這絕對是媒體播放的最佳實踐,可是這裏要問一個重要問題「若是服務中止了,用戶會馬上察覺到嗎?」。前臺服務應該僅被用於關鍵的、可被馬上察覺的場景。ide
注意:要成爲前臺服務須要在服務中包含一個通知以便讓用戶注意到這個服務正在運行。若是你以爲你的使用場景不須要這個通知,那麼前臺服務對你可能不是正確的選擇(是的,成爲前臺服務並不要求必定運行在後臺,見下文)。
在接收關鍵生命週期方法時會讓一個進程臨時提高爲前臺進程,包括任何服務的生命週期方法(onCreate(), onStartCommand() 和 onDestroy()) 和任何廣播接收器 onReceive() 方法。這樣作確保了這些組件的操做是有效的原子操做,每一個組件都能執行完成而不被殺掉。工具
等下,我想我已經談到了當前的 activity?你會發現 activity 可見的時候不必定在前臺。一個簡單的例子是前臺的 activity 使用對話框啓動了一個新的 activity 或者一個透明 activity 。另外一個例子是當你調用運行時權限對話框時(事實上它就是一個 activity!)。學習
在收到 onStart() 和收到 onStop() 方法期間的 activity 是可見 activity 。在這兩個方法調用之間,你能夠作全部可見 activity 能作的事情(實時更新屏幕等)。測試
和前臺 activity 相似,可見 activity 的 bound service 和 content provider 也處於可見進程狀態。這一樣是爲了保證使用中的 activity 所依賴的進程不會被過早地殺掉。spa
但請記住,只是可見並不意味着不能被殺掉。若是來自前臺進程的內存壓力過大,可見進程仍有可能被殺掉。從用戶的角度看,這意味着當前 activity 背後的可見 activity 會被黑屏代替。固然,若是你正確地重建你的 activity ,在前臺 activity 關閉以後你的進程和 activity 會馬上恢復而沒有數據損失。線程
注意:你的 activity 和進程即便可見也可能被殺掉是由於startActivityForResult()+onActivityResult() 或 requestPermissions()+onRequestPermissionsResult() 流程沒有得到回調類的實例。若是你的整個進程死了,那麼全部的回調類實例也死了。若是你看到使用回調方式的庫,你應該意識到這在低內存壓力狀況下沒法完成。
若是你的進程不屬於以上兩種類別,而你有一個啓動的服務(started service),那麼它被看做是一個服務進程。對於許多在後臺作處理(如加載數據)而沒有當即成爲前臺服務的應用都屬於這種狀況。
這沒有問題!絕大多數狀況,這是後臺處理的最佳方式。這種進程只有在前面講的可見進程和前臺進程作了太多事情須要更多資源的時候纔會被殺掉。
請特別注意從 onStartCommand() 返回的常量,若是你的服務因爲內存壓力被殺掉,它表示控制什麼發生什麼:
一、START_STICKY 表示你但願系統可用的時候自動重啓你的服務,但你不關心是否能得到最後一次的 Intent (例如,你能夠重建本身的狀態或者控制本身的 start/stop 生命週期)。
二、START_REDELIVER_INTENT 是爲那些在被殺死以後重啓時從新得到 Intent 的服務的,直到你用傳遞給 onStartCommand() 方法的 startId 參數調用 stopSelf() 爲止。這裏你會使用 Intent 和 startId 做爲隊列完成工做。
三、START_NOT_STICKY 用於那些殺掉也不要緊的服務。這適合那些管理週期性任務的服務,它們只是等待下一個時間窗口工做。
好比說你的 Activity 一開始是前臺 Activity,可是用戶點了 home 鍵致使 onStop() 方法被調用。假設你以前一直是高優先級進程類別,這時你的進程將變爲後臺進程類別。在通常操做場景下,設備上的許多內存就是用在這上面的,讓你從新回到以前打開過的某個 activity 。
Android 不是爲了殺而殺的(記住:從頭啓動是有代價的),因此這些進程會保留一段時間,直到更高優先級進程須要內存的時候才被回收,而且是按照最近最少使用順序(最老的會被優先回收)。然而,當他們被殺掉的時候和可見 activity 處理狀況同樣,你應該可以在不丟失用戶狀態的狀況下重建這些 activity 。
在任何層次中,空進程都是最低優先級的。若是不屬於以上類別,那它就是這種。這裏沒有活躍的組件,只是出於緩存的目的而被保留(爲了更加有效地使用內存而不是徹底釋放掉),只要 Android 須要能夠隨時殺掉它們。
當咱們談論進程優先級的時候是以 activity、service 這樣的組件來講的,但請記住這些優先級是在進程的級別上,不是組件級別上。只要一個組件(好比一個前臺服務)就會將整個進程變爲前臺進程。絕大多數應用是單進程的,若是你有生命週期差別很大的不一樣部分或者某個部分很是重量型,那麼強烈建議你把它們分爲不一樣的進程,讓重量級進程儘早被回收。
一樣重要的是,你的進程屬於什麼類別是組件層面發生的事情決定的。這意味着把很是重要的長時間運行的操做放在 activity 所在進程的一個獨立線程中的作法,在進程忽然變成後臺進程的時候可能會遇到問題。使用你能用到的工具(一個服務或基於優先級的前臺服務)來確保系統知道你在作什麼。
整個系統這樣工做都是爲了用戶。作個好公民,作好你的應用,始終讓本身工做在合適的優先級上。請記住,做爲一個開發者,你使用的手機可能比你用戶的最差手機快得多得多,你可能歷來不會看到可見進程被殺死,遠少於服務進程,可是這不意味着它不會發生!
我仍然建議你購買一個很是低端的 Android 設備用於測試,同時你也能夠在高端設備上測試被殺掉時應用是如何響應的。要在包級別殺掉應用,請使用:
adb shell am force-stop com.example.packagename
若是你有多個進程,能夠在第二欄找到進程 id(PID)(如,下面第一個數字):
adb shell ps | grep com.example.packagename
而後這樣殺掉:
adb shell kill PID
不論內存壓力多大,確保你的應用在儘量多的設備上良好運行的第一步是測試你的應用在被殺掉時是如何響應的。