Android 守護(hù)進(jìn)程的實現(xiàn)方式

項目源碼:【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>

  1. 提高進(jìn)程優(yōu)先級,降低被回收或殺死概率
  2. 在進(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

AndroidLow Memory Killer基于LinuxOOM機(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)用JobScheduleschedule來啟動任務(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)了不是 ?? ~

#參考資料

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容