轉(zhuǎn)載一篇不錯(cuò)的文章脉幢,解決了我一下關(guān)于如何保留android進(jìn)程的技術(shù)。
Android 進(jìn)程编氯瘢活
目前市面上的應(yīng)用嫌松,貌似除了微信和手Q都會(huì)比較擔(dān)心被用戶(hù)或者系統(tǒng)(廠商)殺死問(wèn)題。本文對(duì) Android 進(jìn)程拉活進(jìn)行一個(gè)總結(jié)奕污。
Android 進(jìn)程拉活包括兩個(gè)層面:
A. 提供進(jìn)程優(yōu)先級(jí)萎羔,降低進(jìn)程被殺死的概率
B. 在進(jìn)程被殺死后,進(jìn)行拉活
本文下面就從這兩個(gè)方面做一下總結(jié)碳默。
1. 進(jìn)程的優(yōu)先級(jí)
Android 系統(tǒng)將盡量長(zhǎng)時(shí)間地保持應(yīng)用進(jìn)程贾陷,但為了新建進(jìn)程或運(yùn)行更重要的進(jìn)程,最終需要清除舊進(jìn)程來(lái)回收內(nèi)存嘱根。 為了確定保留或終止哪些進(jìn)程髓废,系統(tǒng)會(huì)根據(jù)進(jìn)程中正在運(yùn)行的組件以及這些組件的狀態(tài),將每個(gè)進(jìn)程放入“重要性層次結(jié)構(gòu)”中该抒。 必要時(shí)慌洪,系統(tǒng)會(huì)首先消除重要性最低的進(jìn)程,然后是清除重要性稍低一級(jí)的進(jìn)程凑保,依此類(lèi)推冈爹,以回收系統(tǒng)資源。
進(jìn)程的重要性欧引,劃分5級(jí):
前臺(tái)進(jìn)程(Foreground process)
可見(jiàn)進(jìn)程(Visible process)
服務(wù)進(jìn)程(Service process)
后臺(tái)進(jìn)程(Background process)
空進(jìn)程(Empty process)
前臺(tái)進(jìn)程的重要性最高犯助,依次遞減,空進(jìn)程的重要性最低维咸,下面分別來(lái)闡述每種級(jí)別的進(jìn)程
1.1. 前臺(tái)進(jìn)程 —— Foreground process
用戶(hù)當(dāng)前操作所必需的進(jìn)程。通常在任意給定時(shí)間前臺(tái)進(jìn)程都為數(shù)不多。只有在內(nèi)存不足以支持它們同時(shí)繼續(xù)運(yùn)行這一萬(wàn)不得已的情況下癌蓖,系統(tǒng)才會(huì)終止它們瞬哼。
A. 擁有用戶(hù)正在交互的 Activity(已調(diào)用 onResume())
B. 擁有某個(gè) Service,后者綁定到用戶(hù)正在交互的 Activity
C. 擁有正在“前臺(tái)”運(yùn)行的 Service(服務(wù)已調(diào)用 startForeground())
D. 擁有正執(zhí)行一個(gè)生命周期回調(diào)的 Service(onCreate()租副、onStart() 或 onDestroy())
E. 擁有正執(zhí)行其 onReceive() 方法的 BroadcastReceiver
1.2. 可見(jiàn)進(jìn)程 —— Visible process
沒(méi)有任何前臺(tái)組件坐慰、但仍會(huì)影響用戶(hù)在屏幕上所見(jiàn)內(nèi)容的進(jìn)程∮蒙可見(jiàn)進(jìn)程被視為是極其重要的進(jìn)程结胀,除非為了維持所有前臺(tái)進(jìn)程同時(shí)運(yùn)行而必須終止,否則系統(tǒng)不會(huì)終止這些進(jìn)程责循。
A. 擁有不在前臺(tái)糟港、但仍對(duì)用戶(hù)可見(jiàn)的 Activity(已調(diào)用 onPause())。
B. 擁有綁定到可見(jiàn)(或前臺(tái))Activity 的 Service
1.3. 服務(wù)進(jìn)程 —— Service process
盡管服務(wù)進(jìn)程與用戶(hù)所見(jiàn)內(nèi)容沒(méi)有直接關(guān)聯(lián)院仿,但是它們通常在執(zhí)行一些用戶(hù)關(guān)心的操作(例如秸抚,在后臺(tái)播放音樂(lè)或從網(wǎng)絡(luò)下載數(shù)據(jù))。因此歹垫,除非內(nèi)存不足以維持所有前臺(tái)進(jìn)程和可見(jiàn)進(jìn)程同時(shí)運(yùn)行剥汤,否則系統(tǒng)會(huì)讓服務(wù)進(jìn)程保持運(yùn)行狀態(tài)。
A. 正在運(yùn)行 startService() 方法啟動(dòng)的服務(wù)排惨,且不屬于上述兩個(gè)更高類(lèi)別進(jìn)程的進(jìn)程吭敢。
1.4. 后臺(tái)進(jìn)程 —— Background process
后臺(tái)進(jìn)程對(duì)用戶(hù)體驗(yàn)沒(méi)有直接影響,系統(tǒng)可能隨時(shí)終止它們暮芭,以回收內(nèi)存供前臺(tái)進(jìn)程鹿驼、可見(jiàn)進(jìn)程或服務(wù)進(jìn)程使用。 通常會(huì)有很多后臺(tái)進(jìn)程在運(yùn)行谴麦,因此它們會(huì)保存在 LRU 列表中蠢沿,以確保包含用戶(hù)最近查看的 Activity 的進(jìn)程最后一個(gè)被終止。如果某個(gè) Activity 正確實(shí)現(xiàn)了生命周期方法匾效,并保存了其當(dāng)前狀態(tài)舷蟀,則終止其進(jìn)程不會(huì)對(duì)用戶(hù)體驗(yàn)產(chǎn)生明顯影響,因?yàn)楫?dāng)用戶(hù)導(dǎo)航回該 Activity 時(shí)面哼,Activity 會(huì)恢復(fù)其所有可見(jiàn)狀態(tài)野宜。
A. 對(duì)用戶(hù)不可見(jiàn)的 Activity 的進(jìn)程(已調(diào)用 Activity的onStop() 方法)
1.5. 空進(jìn)程 —— Empty process
保留這種進(jìn)程的的唯一目的是用作緩存,以縮短下次在其中運(yùn)行組件所需的啟動(dòng)時(shí)間魔策。 為使總體系統(tǒng)資源在進(jìn)程緩存和底層內(nèi)核緩存之間保持平衡匈子,系統(tǒng)往往會(huì)終止這些進(jìn)程。
2. Android 進(jìn)程回收策略
Android 中對(duì)于內(nèi)存的回收闯袒,主要依靠 Lowmemorykiller 來(lái)完成虎敦,是一種根據(jù) OOM_ADJ 閾值級(jí)別觸發(fā)相應(yīng)力度的內(nèi)存回收的機(jī)制游岳。
關(guān)于 OOM_ADJ 的說(shuō)明如下:
其中紅色部分代表比較容易被殺死的 Android 進(jìn)程(OOM_ADJ>=4),綠色部分表示不容易被殺死的 Android 進(jìn)程,其他表示非 Android 進(jìn)程(純 Linux 進(jìn)程)其徙。在 Lowmemorykiller 回收內(nèi)存時(shí)會(huì)根據(jù)進(jìn)程的級(jí)別優(yōu)先殺死 OOM_ADJ 比較大的進(jìn)程胚迫,對(duì)于優(yōu)先級(jí)相同的進(jìn)程則進(jìn)一步受到進(jìn)程所占內(nèi)存和進(jìn)程存活時(shí)間的影響。
Android 手機(jī)中進(jìn)程被殺死可能有如下情況:
綜上唾那,可以得出減少進(jìn)程被殺死概率無(wú)非就是想辦法提高進(jìn)程優(yōu)先級(jí)访锻,減少進(jìn)程在內(nèi)存不足等情況下被殺死的概率。
3. 提升進(jìn)程優(yōu)先級(jí)的方案
3.1. 利用 Activity 提升權(quán)限
3.1.1. 方案設(shè)計(jì)思想
監(jiān)控手機(jī)鎖屏解鎖事件闹获,在屏幕鎖屏?xí)r啟動(dòng)1個(gè)像素的 Activity期犬,在用戶(hù)解鎖時(shí)將 Activity 銷(xiāo)毀掉。注意該 Activity 需設(shè)計(jì)成用戶(hù)無(wú)感知避诽。
通過(guò)該方案龟虎,可以使進(jìn)程的優(yōu)先級(jí)在屏幕鎖屏?xí)r間由4提升為最高優(yōu)先級(jí)1。
3.1.2. 方案適用范圍
適用場(chǎng)景: 本方案主要解決第三方應(yīng)用及系統(tǒng)管理工具在檢測(cè)到鎖屏事件后一段時(shí)間(一般為5分鐘以?xún)?nèi))內(nèi)會(huì)殺死后臺(tái)進(jìn)程茎用,已達(dá)到省電的目的問(wèn)題遣总。
適用版本: 適用于所有的 Android 版本。
3.1.3. 方案具體實(shí)現(xiàn)
首先定義 Activity轨功,并設(shè)置 Activity 的大小為1像素:
其次旭斥,從 AndroidManifest 中通過(guò)如下屬性,排除 Activity 在 RecentTask 中的顯示:
最后古涧,控制 Activity 為透明:
Activity 啟動(dòng)與銷(xiāo)毀時(shí)機(jī)的控制:
3.2. 利用 Notification 提升權(quán)限
3.2.1. 方案設(shè)計(jì)思想
Android 中 Service 的優(yōu)先級(jí)為4垂券,通過(guò) setForeground 接口可以將后臺(tái) Service 設(shè)置為前臺(tái) Service,使進(jìn)程的優(yōu)先級(jí)由4提升為2羡滑,從而使進(jìn)程的優(yōu)先級(jí)僅僅低于用戶(hù)當(dāng)前正在交互的進(jìn)程菇爪,與可見(jiàn)進(jìn)程優(yōu)先級(jí)一致,使進(jìn)程被殺死的概率大大降低柒昏。
3.2.2. 方案實(shí)現(xiàn)挑戰(zhàn)
從 Android2.3 開(kāi)始調(diào)用 setForeground 將后臺(tái) Service 設(shè)置為前臺(tái) Service 時(shí)凳宙,必須在系統(tǒng)的通知欄發(fā)送一條通知,也就是前臺(tái) Service 與一條可見(jiàn)的通知時(shí)綁定在一起的职祷。
對(duì)于不需要常駐通知欄的應(yīng)用來(lái)說(shuō)氏涩,該方案雖好,但卻是用戶(hù)感知的有梆,無(wú)法直接使用是尖。
3.2.3. 方案挑戰(zhàn)應(yīng)對(duì)措施
通過(guò)實(shí)現(xiàn)一個(gè)內(nèi)部 Service,在 LiveService 和其內(nèi)部 Service 中同時(shí)發(fā)送具有相同 ID 的 Notification泥耀,然后將內(nèi)部 Service 結(jié)束掉饺汹。隨著內(nèi)部 Service 的結(jié)束,Notification 將會(huì)消失痰催,但系統(tǒng)優(yōu)先級(jí)依然保持為2兜辞。
3.2.4. 方案適用范圍
適用于目前已知所有版本迎瞧。
3.2.5. 方案具體實(shí)現(xiàn)
4. 進(jìn)程死后拉活的方案
本人覺(jué)得比較通用的解決方案
4.1. 利用系統(tǒng)廣播拉活
4.1.1. 方案設(shè)計(jì)思想
在發(fā)生特定系統(tǒng)事件時(shí),系統(tǒng)會(huì)發(fā)出響應(yīng)的廣播弦疮,通過(guò)在 AndroidManifest 中“靜態(tài)”注冊(cè)對(duì)應(yīng)的廣播監(jiān)聽(tīng)器夹攒,即可在發(fā)生響應(yīng)事件時(shí)拉活。
常用的用于拉活的廣播事件包括:
4.1.2. 方案適用范圍
適用于全部 Android 平臺(tái)胁塞。但存在如下幾個(gè)缺點(diǎn):
1) 廣播接收器被管理軟件、系統(tǒng)軟件通過(guò)“自啟管理”等功能禁用的場(chǎng)景無(wú)法接收到廣播压语,從而無(wú)法自啟啸罢。
2) 系統(tǒng)廣播事件不可控,只能保證發(fā)生事件時(shí)拉活進(jìn)程胎食,但無(wú)法保證進(jìn)程掛掉后立即拉活扰才。
因此,該方案主要作為備用手段厕怜。
4.2. 利用第三方應(yīng)用廣播拉活
4.2.1. 方案設(shè)計(jì)思想
該方案總的設(shè)計(jì)思想與接收系統(tǒng)廣播類(lèi)似衩匣,不同的是該方案為接收第三方 Top 應(yīng)用廣播。
通過(guò)反編譯第三方 Top 應(yīng)用粥航,如:手機(jī)QQ琅捏、微信、支付寶递雀、UC瀏覽器等柄延,以及友盟、信鴿缀程、個(gè)推等 SDK搜吧,找出它們外發(fā)的廣播,在應(yīng)用中進(jìn)行監(jiān)聽(tīng)杨凑,這樣當(dāng)這些應(yīng)用發(fā)出廣播時(shí)滤奈,就會(huì)將我們的應(yīng)用拉活。
4.2.2. 方案適用范圍
該方案的有效程度除與系統(tǒng)廣播一樣的因素外撩满,主要受如下因素限制:
1) 反編譯分析過(guò)的第三方應(yīng)用的多少
2) 第三方應(yīng)用的廣播屬于應(yīng)用私有蜒程,當(dāng)前版本中有效的廣播,在后續(xù)版本隨時(shí)就可能被移除或被改為不外發(fā)鹦牛。
這些因素都影響了拉活的效果搞糕。
4.3. 利用系統(tǒng)Service機(jī)制拉活
4.3.1. 方案設(shè)計(jì)思想
將 Service 設(shè)置為 START_STICKY,利用系統(tǒng)機(jī)制在 Service 掛掉后自動(dòng)拉活:
4.3.2. 方案適用范圍
如下兩種情況無(wú)法拉活:
Service 第一次被異常殺死后會(huì)在5秒內(nèi)重啟曼追,第二次被殺死會(huì)在10秒內(nèi)重啟窍仰,第三次會(huì)在20秒內(nèi)重啟,一旦在短時(shí)間內(nèi) Service 被殺死達(dá)到5次礼殊,則系統(tǒng)不再拉起驹吮。
進(jìn)程被取得 Root 權(quán)限的管理工具或系統(tǒng)工具通過(guò) forestop 停止掉针史,無(wú)法重啟。
4.4. 利用Native進(jìn)程拉活
4.4.1. 方案設(shè)計(jì)思想
主要思想:利用 Linux 中的 fork 機(jī)制創(chuàng)建 Native 進(jìn)程碟狞,在 Native 進(jìn)程中監(jiān)控主進(jìn)程的存活啄枕,當(dāng)主進(jìn)程掛掉后,在 Native 進(jìn)程中立即對(duì)主進(jìn)程進(jìn)行拉活族沃。
主要原理:在 Android 中所有進(jìn)程和系統(tǒng)組件的生命周期受 ActivityManagerService 的統(tǒng)一管理频祝。而且,通過(guò) Linux 的 fork 機(jī)制創(chuàng)建的進(jìn)程為純 Linux 進(jìn)程脆淹,其生命周期不受 Android 的管理常空。
4.4.2. 方案實(shí)現(xiàn)挑戰(zhàn)
挑戰(zhàn)一:在 Native 進(jìn)程中如何感知主進(jìn)程死亡。
要在 Native 進(jìn)程中感知主進(jìn)程是否存活有兩種實(shí)現(xiàn)方式:
在 Native 進(jìn)程中通過(guò)死循環(huán)或定時(shí)器盖溺,輪訓(xùn)判斷主進(jìn)程是否存活漓糙,檔主進(jìn)程不存活時(shí)進(jìn)行拉活。該方案的很大缺點(diǎn)是不停的輪詢(xún)執(zhí)行判斷邏輯烘嘱,非常耗電昆禽。
在主進(jìn)程中創(chuàng)建一個(gè)監(jiān)控文件,并且在主進(jìn)程中持有文件鎖蝇庭。在拉活進(jìn)程啟動(dòng)后申請(qǐng)文件鎖將會(huì)被堵塞醉鳖,一旦可以成功獲取到鎖,說(shuō)明主進(jìn)程掛掉遗契,即可進(jìn)行拉活辐棒。由于 Android 中的應(yīng)用都運(yùn)行于虛擬機(jī)之上,Java 層的文件鎖與 Linux 層的文件鎖是不同的牍蜂,要實(shí)現(xiàn)該功能需要封裝 Linux 層的文件鎖供上層調(diào)用漾根。
封裝 Linux 文件鎖的代碼如下:
Native 層中堵塞申請(qǐng)文件鎖的部分代碼:
挑戰(zhàn)二:在 Native 進(jìn)程中如何拉活主進(jìn)程。
通過(guò) Native 進(jìn)程拉活主進(jìn)程的部分代碼如下鲫竞,即通過(guò) am 命令進(jìn)行拉活辐怕。通過(guò)指定“—include-stopped-packages”參數(shù)來(lái)拉活主進(jìn)程處于 forestop 狀態(tài)的情況。
挑戰(zhàn)三:如何保證 Native 進(jìn)程的唯一从绘。
從可擴(kuò)展性和進(jìn)程唯一等多方面考慮寄疏,將 Native 進(jìn)程設(shè)計(jì)層 C/S 結(jié)構(gòu)模式,主進(jìn)程與 Native 進(jìn)程通過(guò) Localsocket 進(jìn)行通信僵井。在Native進(jìn)程中利用 Localsocket 保證 Native 進(jìn)程的唯一性陕截,不至于出現(xiàn)創(chuàng)建多個(gè) Native 進(jìn)程以及 Native 進(jìn)程變成僵尸進(jìn)程等問(wèn)題。
4.4.3. 方案適用范圍
該方案主要適用于 Android5.0 以下版本手機(jī)批什。
該方案不受 forcestop 影響农曲,被強(qiáng)制停止的應(yīng)用依然可以被拉活,在 Android5.0 以下版本拉活效果非常好。
對(duì)于 Android5.0 以上手機(jī)乳规,系統(tǒng)雖然會(huì)將native進(jìn)程內(nèi)的所有進(jìn)程都?xì)⑺佬卧幔@里其實(shí)就是系統(tǒng)“依次”殺死進(jìn)程時(shí)間與拉活邏輯執(zhí)行時(shí)間賽跑的問(wèn)題,如果可以跑的比系統(tǒng)邏輯快暮的,依然可以有效拉起笙以。記得網(wǎng)上有人做過(guò)實(shí)驗(yàn),該結(jié)論是成立的冻辩,在某些 Android 5.0 以上機(jī)型有效猖腕。
4.5. 利用 JobScheduler 機(jī)制拉活
4.5.1. 方案設(shè)計(jì)思想
Android5.0 以后系統(tǒng)對(duì) Native 進(jìn)程等加強(qiáng)了管理,Native 拉活方式失效微猖。系統(tǒng)在 Android5.0 以上版本提供了 JobScheduler 接口谈息,系統(tǒng)會(huì)定時(shí)調(diào)用該進(jìn)程以使應(yīng)用進(jìn)行一些邏輯操作。
在本項(xiàng)目中凛剥,我對(duì) JobScheduler 進(jìn)行了進(jìn)一步封裝,兼容 Android5.0 以下版本轻姿。封裝后 JobScheduler 接口的使用如下:
4.5.2. 方案適用范圍
該方案主要適用于 Android5.0 以上版本手機(jī)犁珠。
該方案在 Android5.0 以上版本中不受 forcestop 影響,被強(qiáng)制停止的應(yīng)用依然可以被拉活互亮,在 Android5.0 以上版本拉活效果非常好犁享。
僅在小米手機(jī)可能會(huì)出現(xiàn)有時(shí)無(wú)法拉活的問(wèn)題。
4.6. 利用賬號(hào)同步機(jī)制拉活
4.6.1. 方案設(shè)計(jì)思想
Android 系統(tǒng)的賬號(hào)同步機(jī)制會(huì)定期同步賬號(hào)進(jìn)行豹休,該方案目的在于利用同步機(jī)制進(jìn)行進(jìn)程的拉活炊昆。添加賬號(hào)和設(shè)置同步周期的代碼如下:
該方案需要在 AndroidManifest 中定義賬號(hào)授權(quán)與同步服務(wù)。
4.6.2. 方案適用范圍
該方案適用于所有的 Android 版本威根,包括被 forestop 掉的進(jìn)程也可以進(jìn)行拉活凤巨。
最新 Android 版本(Android N)中系統(tǒng)好像對(duì)賬戶(hù)同步這里做了變動(dòng),該方法不再有效洛搀。
5. 其他有效拉活方案
經(jīng)研究發(fā)現(xiàn)還有其他一些系統(tǒng)拉活措施可以使用敢茁,但在使用時(shí)需要用戶(hù)授權(quán),用戶(hù)感知比較強(qiáng)烈留美。
這些方案包括:
利用系統(tǒng)通知管理權(quán)限進(jìn)行拉活
利用輔助功能拉活彰檬,將應(yīng)用加入廠商或管理軟件白名單。
這些方案需要結(jié)合具體產(chǎn)品特性來(lái)搞谎砾。
上面所有解釋這些方案都是考慮的無(wú) Root 的情況逢倍。
其他還有一些技術(shù)之外的措施,比如說(shuō)應(yīng)用內(nèi) Push 通道的選擇:
國(guó)外版應(yīng)用:接入 Google 的 GCM景图。
國(guó)內(nèi)版應(yīng)用:根據(jù)終端不同较雕,在小米手機(jī)(包括 MIUI)接入小米推送、華為手機(jī)接入華為推送症歇;其他手機(jī)可以考慮接入騰訊信鴿或極光推送與小米推送做 A/B Test郎笆。