Android 进程保活手段分析
如何進行進程?;?#xff0c;首先我們應該先分析一下進程被殺死的原因開始
Android進程被殺死的場景分析:
從 Android 的進程被殺死的場景分析,查看被殺死的原因
一.Android手機的進程回收策略
Android 的內(nèi)存回收主要靠 LowMemoryKiller 完成,Low Memorry Killer的機制主要是通過進程的oom_adj和oom_score來進行內(nèi)存的處理的,關于 OOM_ADJ的說明如下:
1. 每一個進程都有一個oom_adj值,取值范圍[-17,15]。
2. 每一個進程都有一個oom_score值,它是根據(jù)oom_adj計算出一個值,分數(shù)越大越容易被殺死。
3. 內(nèi)存緊張時,LMK基于oom_adj和oom_score值來決定是否要回收一個進程。
4. oom_adj值越小,越不容易被殺死。當 oom_adj 的值大于等于4時是比較容易被殺死的 Android進程,0-3表示不容易被殺死的Android進程,小于0的為 ?非Android 進程(純 Linux 進程)尤其是-17的 native 進程不受系統(tǒng)管理不會被系統(tǒng)殺死
5. 查看oom_adj和oom_score方法:
cat proc/pid/oom_adj
cat proc/pid/oom_score
所以結合Android 的進程回收機制,若是想要不被殺死或者減少被殺死的可能性,就需要提升進程優(yōu)先級,降低在內(nèi)存不足被系統(tǒng)回收的可能性
二.killBackgroundProcesses殺死進程
ActivityManager的killBackgroundProcesses方法,可以立即殺死與指定包相關聯(lián)的所有后臺進程,這與內(nèi)核殺死那些進程回收內(nèi)存是一樣的,但這些進程如果在將來某一時刻需要使用,會重新啟動。該方法需要權限Android.permission.KILL_BACKGROUND_PROCESSES。源碼解釋如下:
三.force-stop、kill殺死進程
5.0以下源碼
通過 pid 殺死進程,因此通過主進程 fork 出來的 c 進程是不會被殺死的,但是在5.0及以上源碼發(fā)現(xiàn),通過主進程 fork 出來的子進程也會被殺死了,話不多說直接上源碼,
5.0以上源碼
通過源碼可以看到通過 uid 殺死進程,因此 fork 出來的子進程也同樣會被殺死
總結了以上被殺死的場景分析,我們得出兩種技術方案:
Ⅰ. 提升進程優(yōu)先級(降低被殺死的概率)
Ⅱ.進程殺死后,拉活進程
(一)提升進程優(yōu)先級的技術手段:
Android 系統(tǒng)將盡量長時間地保持應用進程,但為了新建進程或運行更重要的進程,最終需要清除舊進程來回收內(nèi)存。 為了確定保留或終止哪些進程,系統(tǒng)會根據(jù)進程中正在運行的組件以及這些組件的狀態(tài),將每個進程放入“重要性層次結構”中。 必要時,系統(tǒng)會首先消除重要性最低的進程,然后是清除重要性稍低一級的進程,依此類推,以回收系統(tǒng)資源。
進程的重要性,劃分5級:
前臺進程(Foreground process)
可見進程(Visible process)
服務進程(Service process)
后臺進程(Background process)
空進程(Empty process)
前臺進程
用戶當前操作所必需的進程。如果一個進程滿足以下任一條件,即視為前臺進程:
托管用戶正在交互的Activity(已調(diào)用Activity的onResume()方法)
托管某個Service,后者綁定到用戶正在交互的 Activity
托管正在“前臺”運行的Service(服務已調(diào)用startForeground())
托管正執(zhí)行一個生命周期回調(diào)的Service(onCreate()、onStart()或onDestroy())
托管正執(zhí)行其onReceive()方法的BroadcastReceiver
通常,在任意給定時間前臺進程都為數(shù)不多。只有在內(nèi)存不足以支持它們同時繼續(xù)運行這一萬不得已的情況下,系統(tǒng)才會終止它們。 此時,設備往往已達到內(nèi)存分頁狀態(tài),因此需要終止一些前臺進程來確保用戶界面正常響應。
可見進程
沒有任何前臺組件、但仍會影響用戶在屏幕上所見內(nèi)容的進程。 如果一個進程滿足以下任一條件,即視為可見進程:
托管不在前臺、但仍對用戶可見的Activity(已調(diào)用其onPause()方法)。例如,如果前臺 Activity 啟動了一個對話框,允許在其后顯示上一 Activity,則有可能會發(fā)生這種情況。
托管綁定到可見(或前臺)Activity 的Service。
可見進程被視為是極其重要的進程,除非為了維持所有前臺進程同時運行而必須終止,否則系統(tǒng)不會終止這些進程。
服務進程
正在運行已使用startService()方法啟動的服務且不屬于上述兩個更高類別進程的進程。盡管服務進程與用戶所見內(nèi)容沒有直接關聯(lián),但是它們通常在執(zhí)行一些用戶關心的操作(例如,在后臺播放音樂或從網(wǎng)絡下載數(shù)據(jù))。因此,除非內(nèi)存不足以維持所有前臺進程和可見進程同時運行,否則系統(tǒng)會讓服務進程保持運行狀態(tài)。
后臺進程
包含目前對用戶不可見的 Activity 的進程(已調(diào)用 Activity 的onStop()方法)。這些進程對用戶體驗沒有直接影響,系統(tǒng)可能隨時終止它們,以回收內(nèi)存供前臺進程、可見進程或服務進程使用。 通常會有很多后臺進程在運行,因此它們會保存在 LRU (最近最少使用)列表中,以確保包含用戶最近查看的 Activity 的進程最后一個被終止。如果某個 Activity 正確實現(xiàn)了生命周期方法,并保存了其當前狀態(tài),則終止其進程不會對用戶體驗產(chǎn)生明顯影響,因為當用戶導航回該 Activity 時,Activity 會恢復其所有可見狀態(tài)。 有關保存和恢復狀態(tài)的信息,請參閱Activity文檔。
空進程
不含任何活動應用組件的進程。保留這種進程的的唯一目的是用作緩存,以縮短下次在其中運行組件所需的啟動時間。 為使總體系統(tǒng)資源在進程緩存和底層內(nèi)核緩存之間保持平衡,系統(tǒng)往往會終止這些進程。
根據(jù)進程中當前活動組件的重要程度,Android 會將進程評定為它可能達到的最高級別。例如,如果某進程托管著服務和可見 Activity,則會將此進程評定為可見進程,而不是服務進程。
前臺進程的重要性最高,依次遞減,空進程的重要性最低,下面分別來闡述每種級別的進程,進程的回收策略如上,進程的介紹可詳見http://www.jianshu.com/p/8a95f1f82ede?utm_source=desktop&utm_medium=timeline中介紹
目前常見的技術手段如下:
1) 1像素懸浮層
監(jiān)控手機鎖屏解鎖事件,在屏幕鎖屏時啟動1個像素的 Activity,在用戶解鎖時將 Activity 銷毀掉。注意該 Activity 需設計成用戶無感知。
通過該方案,可以使進程的優(yōu)先級在屏幕鎖屏時間由4提升為最高優(yōu)先級1,主要解決第三方應用及系統(tǒng)管理工具在檢測到鎖屏事件后一段時間(一般為5分鐘以內(nèi))內(nèi)會殺死后臺進程,已達到省電的目的問題
實現(xiàn)方案:
首先定義 Activity,并設置 Activity 的大小為1像素:
其次,從 AndroidManifest 中通過如下屬性,排除 Activity 在 RecentTask 中的顯示:
最后,控制 Activity 為透明:
Activity 啟動與銷毀時機的控制:
2) 將Service設置為前臺服務
Android 中 Service 的優(yōu)先級為4,通過 setForeground 接口可以將后臺 Service 設置為前臺 Service,使進程的優(yōu)先級由4提升為2,從而使進程的優(yōu)先級僅僅低于用戶當前正在交互的進程,與可見進程優(yōu)先級一致,使進程被殺死的概率大大降低。
從 Android2.3 開始調(diào)用 setForeground 將后臺 Service 設置為前臺 Service 時,必須在系統(tǒng)的通知欄發(fā)送一條通知,也就是前臺 Service 與一條可見的通知時綁定在一起的。
對于不需要常駐通知欄的應用來說,該方案雖好,但卻是用戶感知的,無法直接使用。
通過實現(xiàn)一個內(nèi)部 Service,在 ?KeepLiveService 和其內(nèi)部 Service 中同時發(fā)送具有相同 ID 的 Notification,然后將內(nèi)部 Service 結束掉。隨著內(nèi)部 Service 的結束,Notification 將會消失,但系統(tǒng)優(yōu)先級依然保持為2。
具體實現(xiàn)方案:
(二)進程死后,拉活進程
1) 在service的onstart方法里返回 START_STICK
主要的幾個返回值:
1. START_STICKY_COMPATIBILITY:START_STICKY的兼容版本,但不保證服務被kill后一定能重啟。
2. START_STICKY:系統(tǒng)就會重新創(chuàng)建這個服務并且調(diào)用onStartCommand()方法,但是它不會重新傳遞最后的Intent對象,這適用于不執(zhí)行命令的媒體播放器(或類似的服務),它只是無限期的運行著并等待工作的到來。
3. START_NOT_STICKY:直到接受到新的Intent對象,才會被重新創(chuàng)建。這是最安全的,用來避免在不需要的時候運行你的服務。
4. START_REDELIVER_INTENT:系統(tǒng)就會重新創(chuàng)建了這個服務,并且用最后的Intent對象調(diào)。等待中的Intent對象會依次被發(fā)送。這適用于如下載文件。
具體實現(xiàn)方案:
將 Service 設置為 START_STICKY,利用系統(tǒng)機制在 Service 掛掉后自動拉活:
如下兩種情況無法拉活:
Service 第一次被異常殺死后會在5秒內(nèi)重啟,第二次被殺死會在10秒內(nèi)重啟,第三次會在20秒內(nèi)重啟,一旦在短時間內(nèi) Service 被殺死達到5次,則系統(tǒng)不再拉起。
進程被取得 Root 權限的管理工具或系統(tǒng)工具通過 forestop 停止掉,無法重啟。
force close和一些清理軟件很容易就清理掉!
2) 添加Manifest文件屬性值為android:persistent=“true”
app.persistent = true不僅僅標志著此apk不能輕易的被kill掉,亦或在被kill掉后能夠自動restart,并且還涉及到了進程的優(yōu)先級。將被設置為CORE_SERVER_ADJ,此值為-12,而核心進程init的值為-16。當前正在前臺運行的進程的值為0。如果應用能設置這個屬性,那就真的可以做到?;?#xff0c;因為他真的可以殺不死,像系統(tǒng)的keyguard進程,media進程,且這些進程的adj都是負數(shù),代表了前臺activity黑屏了他們也不會死。但是這個屬性需要系統(tǒng)shareuid,然后編譯不過,因為需要系統(tǒng)簽名!
因此,要弄這個屬性需獲取兩個關鍵的信息:
(1).在apk的AndroidManifest.xml文件中設置android:persistent=true
(2).此apk需要放入到system/app目錄下,成為一個systemapp
最主要的是讓程序成為系統(tǒng)程序,這個可以做到嗎?如果你技術過硬是可以嘗試下的!首先弄個自動root的apk,加殼放到程序中,然后程序運行的時候,自動運行自動root的apk,獲取root權限,然后在native層中,使用命令的方式把程序移到system/app目錄下,成為系統(tǒng)程序!
3) 覆寫Service的onDestroy方法
在設置里面的正在運行,注意是正在運行里面,點擊關閉,會走onDestroy回調(diào)方法,你在這里可以把自己啟動起來。注意是正常關閉的時候是會自己啟動起來,可是使用第三方的清理軟件360,root過的360,force close這些來搞,壓根不會走到onDestory的方法
4) 監(jiān)聽一堆系統(tǒng)靜態(tài)廣播
在發(fā)生特定系統(tǒng)事件時,系統(tǒng)會發(fā)出響應的廣播,通過在 AndroidManifest 中“靜態(tài)”注冊對應的廣播監(jiān)聽器,即可在發(fā)生響應事件時拉活。
常用的用于拉活的廣播事件包括:
但是有如下問題:
a. 廣播接收器被管理軟件、系統(tǒng)軟件通過“自啟管理”等功能禁用的場景無法接收到廣播,從而無法自啟。
b. 系統(tǒng)廣播事件不可控,只能保證發(fā)生事件時拉活進程,但無法保證進程掛掉后立即拉活。
5) 監(jiān)聽第三方應用的靜態(tài)廣播
與接收系統(tǒng)廣播類似,不同的是該方案為接收第三方應用廣播。
通過反編譯第三方應用,如:手機QQ、微信、支付寶、UC瀏覽器等找出它們外發(fā)的廣播,在應用中進行監(jiān)聽,這樣當這些應用發(fā)出廣播時,就會將我們的應用拉活。
有效程度除與系統(tǒng)廣播一樣的因素外, 第三方應用的廣播屬于應用私有,當前版本中有效的廣播,在后續(xù)版本隨時就可能被移除或被改為不外發(fā)。
6) AlarmManager喚醒
主要是實現(xiàn)也一個監(jiān)聽開機的廣播,和一個周期性的鬧鐘,不過比較致命的是耗電量是很高的
7) 賬戶同步,定時喚醒
android系統(tǒng)里有一個賬戶系統(tǒng),系統(tǒng)定期喚醒賬號更新服務,同步的事件間隔是有限制的,最短1分鐘,利用同步機制進行進程的拉活
難點:需要手動設置賬戶,你如何騙你的用戶給你手動設置賬戶完了之后不卸載你,必須聯(lián)網(wǎng)
添加賬號和設置同步周期的代碼如下:
該方案需要在 AndroidManifest 中定義賬號授權與同步服務。
理論上使用與所有 Android 系統(tǒng),也能解決 force-stop 的拉活,但是前提是需要聯(lián)網(wǎng)狀態(tài)下
注:最新 Android 版本(Android N)中系統(tǒng)好像對賬戶同步這里做了變動,該方法不再有效。
8) 雙服務守護
這個是android里面一個特性,跨進程bind一個service之后,如果被bind的service掛掉,bind他的service會把他拉起來!可以考慮使用遠程服務來實現(xiàn),可是還是不能?;钸M程,可以被殺掉!
9) 多 APP 間互相拉起
比較常見的就是家族 app 之間互相調(diào)起,你監(jiān)聽到我死了,我把你拉起,之間互相拉活對方
10) Native進程拉起
原理就是通過 JNI fork出一個 c 進程,c 進程監(jiān)控主進程是否存活,主要通過管道和文件監(jiān)控的方式實現(xiàn)監(jiān)控,發(fā)現(xiàn)主進程死后,通過調(diào)起一個 service 將主進程拉活
具體的實現(xiàn)方案:
此方案存在兩個問題:
1.如果直接使用 JNI fork 出一個子進程,這樣會存在一個內(nèi)存較大問題,也就是主版占用多大內(nèi)存,native 進程也占用多大內(nèi)存,因此需要去寫一個獨立的 c 進程來降低內(nèi)存大小
2.上面講到5.0以上的系統(tǒng)會將根據(jù) uid 殺進程,因此 native 進程也會被殺死,因此在5.0以上此方案失效的,
11) 雙進程守護
顧名思義開啟兩個 native 進程進行保活,當一個 native 進程被殺死后,另一個 native 進行拉起,具體實現(xiàn)方案可參考:http://blog.csdn.net/marswin89/article/details/50916631
12)JobSchedule機制拉活
Android5.0 以后系統(tǒng)對 Native 進程等加強了管理,Native 拉活方式失效。系統(tǒng)在 Android5.0 以上版本提供了 JobScheduler 接口,系統(tǒng)會定時調(diào)用該進程以使應用進行一些邏輯操作。
具體實現(xiàn)方案:
通過創(chuàng)建一個系統(tǒng)任務JobInfo,添加到 JobSchedule 中,通過系統(tǒng)調(diào)度任務來實現(xiàn)拉活,force-stop 也能實現(xiàn)拉活
但是存在很多兼容性問題,目前在華為6.0以上不生效,以及在7.0系統(tǒng)上不能實現(xiàn),根據(jù)官方文檔說是在 Dos 安全模式下,系統(tǒng)調(diào)度也會被殺死
作者:百度手機助手
鏈接:https://www.jianshu.com/p/8330a73771ca
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯(lián)系作者獲得授權并注明出處。
總結
以上是生活随笔為你收集整理的Android 进程保活手段分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【译】Construction of a
- 下一篇: 【朋克智库】比特币详解1.0——区块链的