項目源碼:【lzan13 / VMDaemonService】
博客地址:【lz’s Notes / Android 守護(hù)進(jìn)程的實現(xiàn)方式】
在我們進(jìn)行應(yīng)用開發(fā)時,會遇到上級的各種需求,其中有一條 剛需:后臺?;?/code>,更有甚者:
我要我們的應(yīng)用永遠(yuǎn)活在用戶的手機(jī)后臺不被殺死 —— 這都 TM 的扯淡
除了系統(tǒng)級別的應(yīng)用能持續(xù)運(yùn)行,所有三方程序都有被殺死的那一天!當(dāng)然QQ/微信/陌陌等會好一些,因為他們已經(jīng)深入設(shè)備的心;
我們能做的只是通過各種手段盡量讓我們的程序在后臺運(yùn)行的時間長一些,或者在被干掉的時候,能夠重新站起來,而且這個也不是每次都有效的,也是不能在所有的設(shè)備的上都有效的;要做到后臺進(jìn)程?;睿覀冃枰龅絻煞奖悖?/p>
- 提高進(jìn)程優(yōu)先級,降低被回收或殺死概率
- 在進(jìn)程被干掉后,進(jìn)行拉起
要實現(xiàn)實現(xiàn)上邊所說,通過下邊幾點(diǎn)來實現(xiàn),首先我們需要了解下進(jìn)程的優(yōu)先級劃分:
#進(jìn)程的優(yōu)先級
Process Importance記錄在ActivityManager.java類中:
/**
* Path:SDK/sources/android-25/android/app/ActivityManager#RunningAppProcessInfo.java
*
* 這個進(jìn)程正在運(yùn)行前臺UI,也就是說,它是當(dāng)前在屏幕頂部的東西,用戶正在進(jìn)行交互的而進(jìn)程
*/
public static final int IMPORTANCE_FOREGROUND = 100;
/**
* 此進(jìn)程正在運(yùn)行前臺服務(wù),即使用戶不是在應(yīng)用中時也執(zhí)行音樂播放,這一般表示該進(jìn)程正在做用戶積極關(guān)心的事情
*/
public static final int IMPORTANCE_FOREGROUND_SERVICE = 125;
/**
* 這個過程不是用戶的直接意識到,但在某種程度上是他們可以察覺的。
*/
public static final int IMPORTANCE_PERCEPTIBLE = 130;
/**
* 此進(jìn)程正在運(yùn)行前臺UI,但設(shè)備處于睡眠狀態(tài),因此用戶不可見,意思是用戶意識不到的進(jìn)程,因為他們看不到或與它交互,
* 但它是相當(dāng)重要,因為用戶解鎖設(shè)備時期望的返回到這個進(jìn)程
*/
public static final int IMPORTANCE_TOP_SLEEPING = 150;
/**
* 進(jìn)程在后臺,但我們不能恢復(fù)它的狀態(tài),所以我們想盡量避免殺死它,不然這個而進(jìn)程就丟了
*/
public static final int IMPORTANCE_CANT_SAVE_STATE = 170;
/**
* 此進(jìn)程正在運(yùn)行某些對用戶主動可見的內(nèi)容,但不是直接顯示在UI,
* 這可能運(yùn)行在當(dāng)前前臺之后的窗口(因此暫停并且其狀態(tài)被保存,不與用戶交互,但在某種程度上對他們可見);
* 也可能在系統(tǒng)的控制下運(yùn)行其他服務(wù),
*/
public static final int IMPORTANCE_VISIBLE = 200;
/**
* 服務(wù)進(jìn)程,此進(jìn)程包含在后臺保持運(yùn)行的服務(wù),這些后臺服務(wù)用戶察覺不到,是無感知的,所以它們可以由系統(tǒng)相對自由地殺死
*/
public static final int IMPORTANCE_SERVICE = 300;
/**
* 后臺進(jìn)程
*/
public static final int IMPORTANCE_BACKGROUND = 400;
/**
* 空進(jìn)程,此進(jìn)程沒有任何正在運(yùn)行的代碼
*/
public static final int IMPORTANCE_EMPTY = 500;
// 此過程不存在。
public static final int IMPORTANCE_GONE = 1000;
#進(jìn)程回收機(jī)制
了解進(jìn)程優(yōu)先級之后,我們還需要知道一個進(jìn)程回收機(jī)制的東西;這里參考AngelDevil在博客園上的一篇文章:
詳情參考:【Android Low Memory Killer】
Android的Low Memory Killer基于Linux的OOM機(jī)制,在Linux中,內(nèi)存是以頁面為單位分配的,當(dāng)申請頁面分配時如果內(nèi)存不足會通過以下流程選擇bad進(jìn)程來殺掉從而釋放內(nèi)存:
alloc_pages -> out_of_memory() -> select_bad_process() -> badness()
在Low Memory Killer中通過進(jìn)程的oom_adj與占用內(nèi)存的大小決定要?dú)⑺赖倪M(jìn)程,oom_adj越小越不容易被殺死;
Low Memory Killer Driver在用戶空間指定了一組內(nèi)存臨界值及與之一一對應(yīng)的一組oom_adj值,當(dāng)系統(tǒng)剩余內(nèi)存位于內(nèi)存臨界值中的一個范圍內(nèi)時,如果一個進(jìn)程的oom_adj值大于或等于這個臨界值對應(yīng)的oom_adj值就會被殺掉。
下邊是表示Process State(即老版本里的OOM_ADJ)數(shù)值對照表,數(shù)值越大,重要性越低,在新版SDK中已經(jīng)在android層去除了小于0的進(jìn)程狀態(tài)
// Path:SDK/sources/android-25/android/app/ActivityManager#RunningAppProcessInfo.java
// 進(jìn)程不存在。
public static final int PROCESS_STATE_NONEXISTENT = -1;
// 進(jìn)程是一個持久的系統(tǒng)進(jìn)程,一般指當(dāng)前 UI 進(jìn)程
public static final int PROCESS_STATE_PERSISTENT = 0;
// 進(jìn)程是一個持久的系統(tǒng)進(jìn)程,正在做和 UI 相關(guān)的操作,但不直接顯示
public static final int PROCESS_STATE_PERSISTENT_UI = 1;
// 進(jìn)程正在托管當(dāng)前的頂級活動。請注意,這涵蓋了用戶可見的所有活動。
public static final int PROCESS_STATE_TOP = 2;
// 進(jìn)程由于系統(tǒng)綁定而托管前臺服務(wù)。
public static final int PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 3;
// 進(jìn)程正在托管前臺服務(wù)。
public static final int PROCESS_STATE_FOREGROUND_SERVICE = 4;
// 與{@link #PROCESS_STATE_TOP}相同,但設(shè)備處于睡眠狀態(tài)。
public static final int PROCESS_STATE_TOP_SLEEPING = 5;
// 進(jìn)程對用戶很重要,是他們知道的東西
public static final int PROCESS_STATE_IMPORTANT_FOREGROUND = 6;
// 進(jìn)程對用戶很重要,但不是他們知道的
public static final int PROCESS_STATE_IMPORTANT_BACKGROUND = 7;
// 進(jìn)程在后臺運(yùn)行備份/恢復(fù)操作
public static final int PROCESS_STATE_BACKUP = 8;
// 進(jìn)程在后臺,但我們不能恢復(fù)它的狀態(tài),所以我們想盡量避免殺死它,不然這個而進(jìn)程就丟了
public static final int PROCESS_STATE_HEAVY_WEIGHT = 9;
// 進(jìn)程在后臺運(yùn)行一個服務(wù),與oom_adj不同,此級別用于正常運(yùn)行在后臺狀態(tài)和執(zhí)行操作狀態(tài)。
public static final int PROCESS_STATE_SERVICE = 10;
// 進(jìn)程在后臺運(yùn)行一個接收器,注意,從oom_adj接收器的角度來看,在較高的前臺級運(yùn)行,但是對于我們的優(yōu)先級,這不是必需的,并且將它們置于服務(wù)之下意味著當(dāng)它們接收廣播時,一些進(jìn)程狀態(tài)中的更少的改變。
public static final int PROCESS_STATE_RECEIVER = 11;
// 進(jìn)程在后臺,但主持家庭活動
public static final int PROCESS_STATE_HOME = 12;
// 進(jìn)程在后臺,但托管最后顯示的活動
public static final int PROCESS_STATE_LAST_ACTIVITY = 13;
// 進(jìn)程正在緩存以供以后使用,并包含活動
public static final int PROCESS_STATE_CACHED_ACTIVITY = 14;
// 進(jìn)程正在緩存供以后使用,并且是包含活動的另一個緩存進(jìn)程的客戶端
public static final int PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 15;
// 進(jìn)程正在緩存以供以后使用,并且為空
public static final int PROCESS_STATE_CACHED_EMPTY = 16;
Process State(即老版本的OOM_ADJ)與Process Importance對應(yīng)關(guān)系,這個方法也是在ActivityManager.java類中,有了這個關(guān)系,就知道可以知道我們的應(yīng)用處于哪個級別,對于我們后邊優(yōu)化有個很好地參考
/**
* Path:SDK/sources/android-25/android/app/ActivityManager#RunningAppProcessInfo.java
*
* 通過這個方法,將Linux底層的 OOM_ADJ級別碼和 android 層面的進(jìn)程重要程度聯(lián)系了起來
*/
public static int procStateToImportance(int procState) {
if (procState == PROCESS_STATE_NONEXISTENT) {
return IMPORTANCE_GONE;
} else if (procState >= PROCESS_STATE_HOME) {
return IMPORTANCE_BACKGROUND;
} else if (procState >= PROCESS_STATE_SERVICE) {
return IMPORTANCE_SERVICE;
} else if (procState > PROCESS_STATE_HEAVY_WEIGHT) {
return IMPORTANCE_CANT_SAVE_STATE;
} else if (procState >= PROCESS_STATE_IMPORTANT_BACKGROUND) {
return IMPORTANCE_PERCEPTIBLE;
} else if (procState >= PROCESS_STATE_IMPORTANT_FOREGROUND) {
return IMPORTANCE_VISIBLE;
} else if (procState >= PROCESS_STATE_TOP_SLEEPING) {
return IMPORTANCE_TOP_SLEEPING;
} else if (procState >= PROCESS_STATE_FOREGROUND_SERVICE) {
return IMPORTANCE_FOREGROUND_SERVICE;
} else {
return IMPORTANCE_FOREGROUND;
}
}
一般情況下,設(shè)備端進(jìn)程被干掉有一下幾種情況
| 進(jìn)程結(jié)束場景 | 結(jié)束方式 | 影響范圍 |
|---|---|---|
| Android 系統(tǒng)自身內(nèi)存回收機(jī)制 | Low Memory Killer | Process State 數(shù)值從大到小 |
| 第三方管理程序清理進(jìn)程 無 Root 權(quán)限 | killBackgroundProcess | Process State 數(shù)值大于6進(jìn)程 |
| 第三方管理程序清理進(jìn)程 有 Root 權(quán)限 | force-stop or Kill | 除當(dāng)前前臺進(jìn)程外所有非系統(tǒng)進(jìn)程 |
| Rom 清除進(jìn)程(用戶手動清理) | force-stop or Kill | 所有非系統(tǒng)進(jìn)程 |
| 用戶手動強(qiáng)制結(jié)束 | force-stop | 第三方應(yīng)用以及非 System 進(jìn)程 |
由以上分析,我們可以可以總結(jié)出,如果想提高我們應(yīng)用后臺運(yùn)行時間,就需要提高當(dāng)前應(yīng)用進(jìn)程優(yōu)先級,來減少被殺死的概率
#守護(hù)進(jìn)程的實現(xiàn)
分析了那么多,現(xiàn)在對Android自身后臺進(jìn)程管理,以及進(jìn)程的回收也有了一個大致的了解,后邊我們要做的就是想盡一切辦法去提高應(yīng)用進(jìn)程優(yōu)先級,降低進(jìn)程被殺的概率;或者是在被殺死后能夠重新啟動后臺守護(hù)進(jìn)程
#1.模擬前臺進(jìn)程
第一種方式就是利用系統(tǒng)漏洞,使用startForeground()將當(dāng)前進(jìn)程偽裝成前臺進(jìn)程,將進(jìn)程優(yōu)先級提高到最高(這里所說的最高是服務(wù)所能達(dá)到的最高,即1);
這種方式在7.x之前都是很好用的,QQ、微信、IReader、Keep 等好多應(yīng)用都是用的這種方式實現(xiàn);因為在7.x 以后的設(shè)備上,這種偽裝前臺進(jìn)程的方式也會顯示出來通知欄提醒,這個是取消不掉的,雖然Google現(xiàn)在還沒有對這種方式加以限制,不過這個已經(jīng)能夠被用戶感知到了,這種方式估計也用不了多久了
下邊看下實現(xiàn)方式,這邊這個VMDaemonService就是一個守護(hù)進(jìn)程服務(wù),其中在服務(wù)的onStartCommand()方法中調(diào)用startForeground()將服務(wù)進(jìn)程設(shè)置為前臺進(jìn)程,當(dāng)運(yùn)行在 API18 以下的設(shè)備是可以直接設(shè)置,API18 以上需要實現(xiàn)一個內(nèi)部的Service,這個內(nèi)部類實現(xiàn)和外部類同樣的操作,然后結(jié)束自己;當(dāng)這個服務(wù)啟動后就會創(chuàng)建一個定時器去發(fā)送廣播,當(dāng)我們的核心服務(wù)被干掉后,就由另外的廣播接收器去接收我們守護(hù)進(jìn)程發(fā)出的廣播,然后喚醒我們的核心服務(wù);
/**
* 以實現(xiàn)內(nèi)部 Service 類的方式實現(xiàn)守護(hù)進(jìn)程,這里是利用 android 漏洞提高當(dāng)前進(jìn)程優(yōu)先級
*
* Created by lzan13 on 2017/3/7.
*/
public class VMDaemonService extends Service {
private final static String TAG = VMDaemonService.class.getSimpleName();
// 定時喚醒的時間間隔,這里為了自己測試方邊設(shè)置了一分鐘
private final static int ALARM_INTERVAL = 1 * 60 * 1000;
// 發(fā)送喚醒廣播請求碼
private final static int WAKE_REQUEST_CODE = 5121;
// 守護(hù)進(jìn)程 Service ID
private final static int DAEMON_SERVICE_ID = -5121;
@Override public void onCreate() {
Log.i(TAG, "VMDaemonService->onCreate");
super.onCreate();
}
@Override public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "VMDaemonService->onStartCommand");
// 利用 Android 漏洞提高進(jìn)程優(yōu)先級,
startForeground(DAEMON_SERVICE_ID, new Notification());
// 當(dāng) SDk 版本大于18時,需要通過內(nèi)部 Service 類啟動同樣 id 的 Service
if (Build.VERSION.SDK_INT >= 18) {
Intent innerIntent = new Intent(this, DaemonInnerService.class);
startService(innerIntent);
}
// 發(fā)送喚醒廣播來促使掛掉的UI進(jìn)程重新啟動起來
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent alarmIntent = new Intent();
alarmIntent.setAction(VMWakeReceiver.DAEMON_WAKE_ACTION);
PendingIntent operation = PendingIntent.getBroadcast(this, WAKE_REQUEST_CODE, alarmIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
ALARM_INTERVAL, operation);
/**
* 這里返回值是使用系統(tǒng) Service 的機(jī)制自動重新啟動,不過這種方式以下兩種方式不適用:
* 1.Service 第一次被異常殺死后會在5秒內(nèi)重啟,第二次被殺死會在10秒內(nèi)重啟,第三次會在20秒內(nèi)重啟,一旦在短時間內(nèi) Service 被殺死達(dá)到5次,則系統(tǒng)不再拉起。
* 2.進(jìn)程被取得 Root 權(quán)限的管理工具或系統(tǒng)工具通過 forestop 停止掉,無法重啟。
*/
return START_STICKY;
}
@Override public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("onBind 未實現(xiàn)");
}
@Override public void onDestroy() {
Log.i(TAG, "VMDaemonService->onDestroy");
super.onDestroy();
}
/**
* 實現(xiàn)一個內(nèi)部的 Service,實現(xiàn)讓后臺服務(wù)的優(yōu)先級提高到前臺服務(wù),這里利用了 android 系統(tǒng)的漏洞,
* 不保證所有系統(tǒng)可用,測試在7.1.1 之前大部分系統(tǒng)都是可以的,不排除個別廠商優(yōu)化限制
*/
public static class DaemonInnerService extends Service {
@Override public void onCreate() {
Log.i(TAG, "DaemonInnerService -> onCreate");
super.onCreate();
}
@Override public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "DaemonInnerService -> onStartCommand");
startForeground(DAEMON_SERVICE_ID, new Notification());
stopSelf();
return super.onStartCommand(intent, flags, startId);
}
@Override public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("onBind 未實現(xiàn)");
}
@Override public void onDestroy() {
Log.i(TAG, "DaemonInnerService -> onDestroy");
super.onDestroy();
}
}
}
當(dāng)我們啟動這個守護(hù)進(jìn)程的時候,就可以使用以下adb命令查看當(dāng)前程序的進(jìn)程情況(需要adb shell進(jìn)去設(shè)備),
為了等下區(qū)分進(jìn)程優(yōu)先級,我啟動了一個普通的后臺進(jìn)程,兩外兩個一個是我們啟動的守護(hù)進(jìn)程,一個是當(dāng)前程序的核心進(jìn)程,可以看到除了后臺進(jìn)程外,另外兩個進(jìn)程都帶有isForeground=true的屬性:
# 這個命令的 services 可以換成 service,這樣會只顯示當(dāng)前,進(jìn)程,不顯示詳細(xì)內(nèi)容
# dumpsys activity services <Your Package Name>
root@vbox86p:/ # dumpsys activity services com.vmloft.develop.daemon
ACTIVITY MANAGER SERVICES (dumpsys activity services)
User 0 active services:
* ServiceRecord{170fe1dd u0 com.vmloft.develop.daemon/.services.VMDaemonService}
intent={cmp=com.vmloft.develop.daemon/.services.VMDaemonService}
packageName=com.vmloft.develop.daemon
processName=com.vmloft.develop.daemon:daemon
baseDir=/data/app/com.vmloft.develop.daemon-1/base.apk
dataDir=/data/data/com.vmloft.develop.daemon
app=ProcessRecord{173fe77f 2370:com.vmloft.develop.daemon:daemon/u0a68}
isForeground=true foregroundId=-5121 foregroundNoti=Notification(pri=0 contentView=com.vmloft.develop.daemon/0x1090077 vibrate=null sound=null defaults=0x0 flags=0x62 color=0xff607d8b vis=PRIVATE)
createTime=-6s196ms startingBgTimeout=--
lastActivity=-6s157ms restartTime=-6s157ms createdFromFg=true
startRequested=true delayedStop=false stopIfKilled=false callStart=true lastStartId=1
* ServiceRecord{2fee4f84 u0 com.vmloft.develop.daemon/.services.VMCoreService}
intent={cmp=com.vmloft.develop.daemon/.services.VMCoreService}
packageName=com.vmloft.develop.daemon
processName=com.vmloft.develop.daemon
baseDir=/data/app/com.vmloft.develop.daemon-1/base.apk
dataDir=/data/data/com.vmloft.develop.daemon
app=ProcessRecord{18c6a1b4 2343:com.vmloft.develop.daemon/u0a68}
isForeground=true foregroundId=-5120 foregroundNoti=Notification(pri=0 contentView=com.vmloft.develop.daemon/0x1090077 vibrate=null sound=null defaults=0x0 flags=0x62 color=0xff607d8b vis=PRIVATE)
createTime=-28s136ms startingBgTimeout=--
lastActivity=-28s136ms restartTime=-28s136ms createdFromFg=true
startRequested=true delayedStop=false stopIfKilled=false callStart=true lastStartId=1
* ServiceRecord{2ef6909e u0 com.vmloft.develop.daemon/.services.VMBackgroundService}
intent={cmp=com.vmloft.develop.daemon/.services.VMBackgroundService}
packageName=com.vmloft.develop.daemon
processName=com.vmloft.develop.daemon:background
baseDir=/data/app/com.vmloft.develop.daemon-1/base.apk
dataDir=/data/data/com.vmloft.develop.daemon
app=ProcessRecord{29f8734c 2388:com.vmloft.develop.daemon:background/u0a68}
createTime=-3s279ms startingBgTimeout=--
lastActivity=-3s262ms restartTime=-3s262ms createdFromFg=true
startRequested=true delayedStop=false stopIfKilled=false callStart=true lastStartId=1
然后我們可以用下邊的命令查看ProcessID
# 這個命令可以查看當(dāng)前DProcessID(數(shù)據(jù)結(jié)果第二列),我們可以看到當(dāng)前程序有兩個進(jìn)程
# ps | grep com.vmloft.develop.daemon
root@vbox86p:/ # ps | grep com.vmloft.develop.daemon
u0_a68 2343 274 1012408 42188 ffffffff f74f1b45 S com.vmloft.develop.daemon
u0_a68 2370 274 997012 26152 ffffffff f74f1b45 S com.vmloft.develop.daemon:daemon
u0_a68 2388 274 997012 25668 ffffffff f74f1b45 S com.vmloft.develop.daemon:background
有了ProcessID之后,我們可以根據(jù)這個ProcessID獲取到當(dāng)前進(jìn)程的優(yōu)先級狀態(tài)Process State,對應(yīng)Linux層的oom_adj
可以看到當(dāng)前核心進(jìn)程的級別為0,因為這個表示當(dāng)前程序運(yùn)行在前臺 UI 界面,守護(hù)進(jìn)程級別為1,因為我們利用漏洞設(shè)置成了前臺進(jìn)程,雖然不可見,但是他的級別也是比較高的,僅次于前臺 UI 進(jìn)程,然后普通后臺進(jìn)程級別為4;當(dāng)我們退到后臺時,可以看到核心進(jìn)程的級別變?yōu)?code>1了,這就是因為我們利用startForeground()將進(jìn)程設(shè)置成前臺進(jìn)程的原因,這樣就降低了進(jìn)程被系統(tǒng)回收的概率了;
# 這個命令就是通過 ProcessID 輸出其對應(yīng) oom_adj
# cat /proc/ProcessID/oom_adj
# 程序在前臺時,查詢進(jìn)程級別
root@vbox86p:/ # cat /proc/2343/oom_adj
0
root@vbox86p:/ # cat /proc/2370/oom_adj
1
root@vbox86p:/ # cat /proc/2388/oom_adj
4
# 當(dāng)程序退到后臺時,再次查看進(jìn)程級別
root@vbox86p:/ # cat /proc/2343/oom_adj
1
root@vbox86p:/ # cat /proc/2370/oom_adj
1
root@vbox86p:/ # cat /proc/2388/oom_adj
4
可以看到這種方式確實能夠提高進(jìn)程優(yōu)先級,但是在一些國產(chǎn)的設(shè)備上還是會被殺死的,比我我測試的時候小米點(diǎn)擊清空最近運(yùn)行的應(yīng)用進(jìn)程就別干掉了;當(dāng)把應(yīng)用加入到設(shè)備白名單里就不會被殺死了,微信就是這樣,人家直接裝上之后就已經(jīng)在白名單里了,我們要做的就是在用戶使用中引導(dǎo)他們將我們的程序設(shè)置進(jìn)白名單,將守護(hù)進(jìn)程和白名單結(jié)合起來,這樣才能保證我們的應(yīng)用持續(xù)或者
#2.JobScheduler機(jī)制喚醒
Android系統(tǒng)在5.x以上版本提供了一個JobSchedule接口,系統(tǒng)會根據(jù)自己實現(xiàn)定時去調(diào)用改接口傳遞的進(jìn)程去實現(xiàn)一些操作,而且這個接口在被強(qiáng)制停止后依然能夠正常的啟動;不過在一些國產(chǎn)設(shè)備上可能無效,比如小米;
下邊是 JobServcie 的實現(xiàn):
/**
* 5.x 以上使用 JobService 實現(xiàn)守護(hù)進(jìn)程,這個守護(hù)進(jìn)程要做的工作很簡單,就是啟動應(yīng)用的核心進(jìn)程
* Created by lzan13 on 2017/3/8.
*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP) public class VMDaemonJobService extends JobService {
private final static String TAG = VMDaemonJobService.class.getSimpleName();
@Override public boolean onStartJob(JobParameters params) {
Log.d(TAG, "onStartJob");
// 這里為了掩飾直接啟動核心進(jìn)程,沒有做其他判斷操作
startService(new Intent(getApplicationContext(), VMCoreService.class));
return false;
}
@Override public boolean onStopJob(JobParameters params) {
Log.d(TAG, "onStopJob");
return false;
}
}
我們要做的就是在需要的時候調(diào)用JobSchedule的schedule來啟動任務(wù);剩下的就不需要關(guān)心了,JobSchedule會幫我們做好,下邊就是我這邊實現(xiàn)的啟動任務(wù)的方法:
/**
* 5.x以上系統(tǒng)啟用 JobScheduler API 進(jìn)行實現(xiàn)守護(hù)進(jìn)程的喚醒操作
*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void startJobScheduler() {
int jobId = 1;
JobInfo.Builder jobInfo = new JobInfo.Builder(jobId, new ComponentName(this, VMDaemonJobService.class));
jobInfo.setPeriodic(10000);
jobInfo.setPersisted(true);
JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
jobScheduler.schedule(jobInfo.build());
}
#3.系統(tǒng) Service START_STICKY 機(jī)制重啟
在實現(xiàn)Service類時,將onStartCommand()返回值設(shè)置為START_STICKY,利用系統(tǒng)機(jī)制在Service掛掉后自動拉活;不過這種方式只適合比較原生一些的系統(tǒng),像小米,華為等這些定制化比較高的第三方廠商,他們都已經(jīng)把這些給限制掉了;
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "VMDaemonService->onStartCommand");
/**
* 這里返回值是使用系統(tǒng) Service 的機(jī)制自動重新啟動,不過這種方式以下兩種方式不適用:
* 1.Service 第一次被異常殺死后會在5秒內(nèi)重啟,第二次被殺死會在10秒內(nèi)重啟,第三次會在20秒內(nèi)重啟,一旦在短時間內(nèi) Service 被殺死達(dá)到5次,則系統(tǒng)不再拉起。
* 2.進(jìn)程被取得 Root 權(quán)限的管理工具或系統(tǒng)工具通過 forestop 停止掉,無法重啟。
* 3.一些定制化比較高的第三方系統(tǒng)也不適用
*/
return START_STICKY;
}
這種方式在以下兩種情況無效:
-
Service第一次被異常殺死后會在5秒內(nèi)重啟,第二次被殺死會在10秒內(nèi)重啟,第三次會在20秒內(nèi)重啟,一旦在短時間內(nèi)Service被殺死達(dá)到5次,這個服務(wù)就不能再次重啟了; - 進(jìn)程被取得
Root權(quán)限的管理工具或系統(tǒng)工具通過fores-top方式停止掉,無法重啟; - 一些定制化比較高的第三方系統(tǒng)也不適用
#4.其他保活方式
- 利用 Native 本地進(jìn)程,這個主要使用到 jni 調(diào)用底層實現(xiàn),而且在 Android 5.x 以后對這個限制也比較高,不適用了,暫時不研究
- 集成第三方SDK互相喚醒,這個只要正常集成了第三方的SDK,并使用了他們對應(yīng)的服務(wù),當(dāng)一個設(shè)備安裝的多個應(yīng)用都集成了某一個第三方SDK時,啟動任意一個 app 都會喚醒其他的 app,不過這個在一些新版的國內(nèi)廠商系統(tǒng)也是做了限制,這種方式并沒有什么效果
- 一像素的 Activity 方式(流氓方式),經(jīng)測試一些手機(jī)系統(tǒng)無法檢測到解鎖和鎖屏,不確定是否系統(tǒng)修改了解鎖或者鎖屏的廣播,還是禁用了這些廣播,因此此方式無效;
#結(jié)語
事事沒有絕對,萬物總有一些漏洞,就算上邊的那些方式不可用了,后邊肯定還會出現(xiàn)其他的方式;我們不能保證我們的應(yīng)用不死,但我們可以提高存活率;
其實最好的方式還是把程序做好,讓程序本身深入人心,別人喜歡你了,就算你被干掉了,他們也會主動的把你拉起來,然后把你加入他們的白名單,然后我們的目的就實現(xiàn)了不是 ?? ~