Android應用互相喚醒的狀況是中國特點,國外由於有 Google Play 的評價系統和基本的審覈機制,不會有國內這麼瘋狂的流氓式設計。這樣作的好處一個是方便收集用戶信息,瞭解用戶習慣,優化產品,再者給用戶推送消息須要APP退出前臺時可以保活,對於IM應用來說更是硬性需求。因此有些你沒裝過或者周圍沒人用的 APP ,隨隨便便都能日活上千萬。html
當前業界的Android進程保活手段主要分爲 黑、白、灰 三種,其大體的實現思路以下:java
不一樣的app進程,用廣播相互喚醒(包括利用系統提供的廣播進行喚醒)linux
場景1:開機,網絡切換、拍照、拍視頻時候,利用系統產生的廣播喚醒appandroid
場景2:接入第三方SDK也會喚醒相應的app進程,如微信sdk會喚醒微信,支付寶sdk會喚醒支付寶。由此發散開去,就會直接觸發了下面的 場景3api
場景3:假如你手機裏裝了支付寶、淘寶、天貓、UC等阿里系的app,那麼你打開任意一個阿里系的app後,有可能就順便把其餘阿里系的app給喚醒了。(只是拿阿里打個比方,其實BAT系都差很少)緩存
沒錯,咱們的Android手機就是一步一步的被上面這些場景給拖卡機的。bash
針對場景1,估計Google已經開始意識到這些問題,因此在最新的Android N取消了 ACTION_NEW_PICTURE(拍照),ACTION_NEW_VIDEO(拍視頻),CONNECTIVITY_ACTION(網絡切換)等三種廣播。微信
啓動前臺Service網絡
白色保活手段很是簡單,就是調用系統api啓動一個前臺的Service進程,這樣會在系統的通知欄生成一個Notification,用來讓用戶知道有這樣一個app在運行着,哪怕當前的app退到了後臺。這樣的目的也很容易理解,就是提升進程的優先級。讓Linux在內存不足的時候不會優先被殺掉,從某種意義上來講這算是最不流氓的一種保活方式了。
app
利用系統的漏洞啓動前臺Service
灰色保活,這種保活手段是應用範圍最普遍。它是利用系統的漏洞來啓動一個前臺的Service進程,與普通的啓動方式區別在於,它不會在系統通知欄處出現一個Notification,看起來就如同運行着一個後臺Service進程同樣。這樣作帶來的好處就是,用戶沒法察覺到你運行着一個前臺進程(由於看不到Notification),但你的進程優先級又是高於普通後臺進程的
在Android系統中,進程的優先級影響着如下三個因素:
系統對於進程的優先級有以下五個分類:
前臺進程
指正在與用戶進行交互的應用進程,該進程數量較少,是最高優先級進程,系統通常不會終止該進程,而判斷爲前臺進程的因素有如下這些
進程中包含處於前臺的正與用戶交互的activity;
進程中包含與前臺activity綁定的service;
進程中包含調用了startForeground()方法的service;
進程中包含正在執行onCreate(), onStart(), 或onDestroy()方法的service;
進程中包含正在執行onReceive()方法的BroadcastReceiver.
可視進程
能被用戶看到,但不能根據根據用戶的動做作出相應的反饋, 因素 進程中包含可見但不處於前臺進程的activity(如:activity執行onPause()時處於可見狀態,但並不處於前臺進程中) 該進程有一個與可見/前臺的activity綁定數據的service
服務進程
沒有可見界面仍在不斷的執行任務的進程,除非在可視進程和前臺進程緊缺資源(如:內存資源)纔會被終止 因素 包含除前臺進程和可視進程的service外的service的進程
後臺進程
一般系統中有大量的後臺進程,終止後臺進程不會影響用戶體驗,隨時爲優先級更高的進程騰出資源而被終止,優先回收長時間沒用使用過的進程。 因素 包含不在前臺或可視進程的activity的進程,也就是已經調用onStop()方法後的activity
空進程
爲提升總體系統性能,系統會保存已經完成生命週期的應用程序 ,存在與內存當中,也就是緩存,爲下次的啓動更加迅速而設計。
上面是進程的分類,進程是怎麼被殺的呢?系統出於體驗和性能上的考慮,app在退到後臺時系統並不會真正的kill掉這個進程,而是將其緩存起來。打開的應用越多,後臺緩存的進程也越多。在系統內存不足的狀況下,系統開始依據自身的一套進程回收機制來判斷要kill掉哪些進程,以騰出內存來供給須要的app, 這套殺進程回收內存的機制就叫 Low Memory Killer。那這個不足怎麼來規定呢,那就是內存閾值,咱們可使用cat /sys/module/lowmemorykiller/parameters/minfree來查看某個手機的內存閾值。
讀到這裏,你或許有一個疑問,假設如今內存不足,空進程都被殺光了,如今要殺後臺進程,可是手機中後臺進程不少,難道要一次性所有都清理掉?固然不是的,進程是有它的優先級的,這個優先級經過進程的adj值來反映,它是linux內核分配給每一個系統進程的一個值,表明進程的優先級,進程回收機制就是根據這個優先級來決定是否進行回收,adj值定義在com.android.server.am.ProcessList類中,這個類路徑是${android-sdk-path}\sources\android-23\com\android\server\am\ProcessList.java。
對於每個運行中的進程,Linux內核都經過proc文件系統暴露這樣一個文件來容許其餘程序修改指定進程的優先級:
/proc/[pid]/oom_score_adj。(修改這個文件須要root
權限)
這個文件容許的值的範圍是:-1000 ~ +1000之間。值越小,表示進程越重要。
當內存很是緊張時,系統便會遍歷全部進程,以肯定哪一個進程須要被殺死以回收內存,此時便會讀取oom_score_adj
這個文件的值。關於這個值的使用,在後面講解進程回收的的時候,咱們會詳細講解。
PS:在Linux 2.6.36以前的版本中,Linux 提供調整優先級的文件是/proc/[pid]/oom_adj
。這個文件容許的值的範圍是-17 ~ +15
之間。數值越小表示進程越重要。 這個文件在新版的Linux中已經廢棄。
但你仍然可使用這個文件,當你修改這個文件的時候,內核會直接進行換算,將結果反映到oom_score_adj
這個文件上。
Android早期版本的實現中也是依賴oom_adj
這個文件。可是在新版本中,已經切換到使用oom_score_adj
這個文件。oom_adj的值越小,進程的優先級越高,普通進程oom_adj值是大於等於0的,而系統進程oom_adj的值是小於0的,咱們能夠經過cat /proc/進程id/oom_adj能夠看到當前進程的adj值。
看到adj值是0,0就表明這個進程是屬於前臺進程,咱們按下Back鍵,將應用至於後臺,再次查看
OOM全稱Out Of Memory,是Linux當中,內存保護機制的一種。該機制會監控那些佔用內存過大,尤爲是瞬間很快消耗大量內存的進程,爲了防止內存耗盡而內核將該進程殺掉。
當Kernel遇到OOM的時候,能夠有2種選擇:
1) 產生kernelpanic(死機)
2) 啓動OOM killer,選擇一個或多個「合適」的進程,幹掉那些選擇中的進程,從而釋放內存。
在Linux中,系統就是經過算分去殺死進程的。至於分數的值,就是這3個參數的值。簡單來講,系統是這樣進行算分的:算分主要分2部分,一部分是系統打分,主要根據該進程的內存使用狀況(oom_score),另外一部分是用戶大份額,就是oom_score_adj。每一個進程的實際得分是綜合這2個參數的值的。而oom_adj只是一箇舊的接口參數,在普通的linux系統中和oom_score_adj是差很少的(可是在android中是頗有用的)。
在Android中,及時用戶退出當前應用程序後,應用程序仍是會存在於系統當中,這是爲了方便程序的再次啓動。可是這樣的話,隨着打開的程序的數量的增長,系統的內存就會不足,從而須要殺掉一些進程來釋放內存空間。至因而否須要殺進程以及殺什麼進程,這個就是由Android的內部機制LowMemoryKiller機制來進行的。
Andorid的Low Memory Killer是在標準的linux lernel的OOM基礎上修改而來的一種內存管理機制。當系統內存不足時,殺死沒必要要的進程釋放其內存。沒必要要的進程的選擇根據有2個:oom_adj和佔用的內存的大小。oom_adj表明進程的優先級,數值越高,優先級月低,越容易被殺死;對應每一個oom_adj均可以有一個空閒進程的閥值。Android Kernel每隔一段時間會檢測當前空閒內存是否低於某個閥值。假如是,則殺死oom_adj最大的沒必要要的進程,若是有多個,就根據oom_score_adj去殺死進程,,直到內存恢復低於閥值的狀態。
LowMemoryKiller的值的設定,主要保存在2個文件之中,分別是/sys/module/lowmemorykiller/parameters/adj與/sys/module/lowmemorykiller/parameters/minfree。adj保存着當前系統殺進程的等級,minfree則是保存着對應的閥值。他們的對應關係以下:
舉個例子說明一下上表,噹噹前系統內存少於55296×4K(即216MB)時,Android就會找出當前oom_adj≥9的進程,根據進程的等級,先把oom_adj數值最大的進程給殺掉,釋放他的內存,當他們的oom_adj相等時,就對比他們的oom_score_adj,而後oom_score_adj越大,也越容易殺掉。
在這裏,也許有人會問,爲何採用LowMemoryKiller而不用OOM呢?咱們來對比一下二者,就能夠得出答案了。
使用LowMemoryKiller可使系統內存較低時,調出進程管理器結束沒必要要的人進程釋放空間。在安卓中,若是等到真正的OOM時,也許進程管理器就已經無法啓動了。
上面其實已經說過,LowMemoryKiller是對多個內存閥值的控制來選擇殺進程的。可是,這些閥值是怎樣聯繫在一塊兒的呢?下面就個人理解,簡單說一下其運行的原理。
首先,LowMemoryKiller是隨着系統的啓動而啓動的。當前主要的LowMemoryKiller的代碼主要在\system\core\lmkd的目錄下,以前的代碼\kernel\drivers\staging\android\lowmemorykiller.c已經再也不使用。
LowMemoryKiller在系統啓動的時候就已經由init進程一併啓動了。LowMemoryKiller啓動就是,就會不斷監測系統的運行狀況和內存狀況,當內存少於minfree限定的閥值的時候,lowMemoryKiller遍歷當前進程的oom_score_adj,把大於對應閥值的進程進行kill操做。例如,在當前設置中,當系統內存少於315M時,系統就會自動把進程中oom_score_adj的值少於1000的殺掉,當系統內存少於216時,系統就會自動把進程中oom_score_adj的值少於529的殺掉,如此類推。
至於oom_adj和oom_score_adj是由誰去控制並寫入的呢?
在系統當中,oom_adj和oom_score_adj是由ActivityManagerService去控制的,上層應用的啓動都離不開AcitivityManagerService的調用與分配資源。有關oom_adj與oom_score_adj會在之後分析ActivityManagerService的時候加入相對詳細的論述。在這裏就不詳細說明。
有關Minfree的值的寫入,其實能夠找到不少個地方,可是在最開始(還在用lowmemorykiller.c)的時候,是能夠在lowmemorykiller.c中設置的。可是如今已經不用lowmemorykiller.c了,因此相對設置的地方也不同了。經查找驗證,Minfree的閥值控制,是由ActivictyManagerService和lowmemorykiller一併控制寫入的。
這種大部分人都瞭解,聽說這個微信也用過的進程保活方案,這方案實際利用了Android前臺service的漏洞。
原理以下
對於 API level < 18 :調用startForeground(ID, new Notification()),發送空的Notification ,圖標則不會顯示。
對於 API level >= 18:在須要提優先級的service A啓動一個InnerService,兩個服務同時startForeground,且綁定一樣的 ID。Stop 掉InnerService ,這樣通知欄圖標即被移除
相互喚醒的意思就是,假如你手機裏裝了支付寶、淘寶、天貓、UC等阿里系的app,那麼你打開任意一個阿里系的app後,有可能就順便把其餘阿里系的app給喚醒了。這個徹底有可能的。此外,開機,網絡切換、拍照、拍視頻時候,利用系統產生的廣播也能喚醒app,不過Android N已經將這三種廣播取消了。
這個是系統自帶的,onStartCommand方法必須具備一個整形的返回值,這個整形的返回值用來告訴系統在服務啓動完畢後,若是被Kill,系統將如何操做,這種方案雖然能夠,可是在某些狀況or某些定製ROM上可能失效
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_REDELIVER_INTENT;
}
複製代碼
START_STICKY
若是系統在onStartCommand返回後被銷燬,系統將會從新建立服務並依次調用onCreate和onStartCommand(注意:根據測試Android2.3.3如下版本只會調用onCreate根本不會調用onStartCommand,Android4.0能夠辦到),這種至關於服務又從新啓動恢復到以前的狀態了)。
START_NOT_STICKY
若是系統在onStartCommand返回後被銷燬,若是返回該值,則在執行完onStartCommand方法後若是Service被殺掉系統將不會重啓該服務。
START_REDELIVER_INTENT
START_STICKY的兼容版本,不一樣的是其不保證服務被殺後必定能重啓。
是做爲進程死後復活的一種手段,native進程方式最大缺點是費電, Native 進程費電的緣由是感知主進程是否存活有兩種實現方式,在 Native 進程中經過死循環或定時器,輪訓判斷主進程是否存活,當主進程不存活時進行拉活。其次5.0以上系統不支持。 可是JobSheduler能夠替代在Android5.0以上native進程方式,這種方式即便用戶強制關閉,也能被拉起來,代碼以下:
JobSheduler@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class MyJobService extends JobService {
@Override
public void onCreate() {
super.onCreate();
startJobSheduler();
}
public void startJobSheduler() {
try {
JobInfo.Builder builder = new JobInfo.Builder(1, new ComponentName(getPackageName(), MyJobService.class.getName()));
builder.setPeriodic(5);
builder.setPersisted(true);
JobScheduler jobScheduler = (JobScheduler) this.getSystemService(Context.JOB_SCHEDULER_SERVICE);
jobScheduler.schedule(builder.build());
} catch (Exception ex) {
ex.printStackTrace();
}
}
@Override
public boolean onStartJob(JobParameters jobParameters) {
return false;
}
@Override
public boolean onStopJob(JobParameters jobParameters) {
return false;
}
}複製代碼
聽說這個是手Q的進程保活方案,基本思想,系統通常是不會殺死前臺進程的。因此要使得進程常駐,咱們只須要在鎖屏的時候在本進程開啓一個Activity,爲了欺騙用戶,讓這個Activity的大小是1像素,而且透明無切換動畫,在開屏幕的時候,把這個Activity關閉掉,因此這個就須要監聽系統鎖屏廣播.