Android應用/進程保活策略總結

Android 應用/進程保活策略總結

1.將Service設置爲前臺服務

思路:啓用前臺服務,主要是startForeground() 。
保活程度:一般情況下不被殺,部分定製ROM會在應用切到後臺即殺 ,會被 用戶手動殺進程(force stop)殺死。
使用場景:大部分音樂播放器通知欄的實現,可以保證後臺聽歌時應用正常運行。

2.在service的onstart方法裏返回 STATR_STICK

思路:其實就是onStartCommand中返回STATR_STICK
保活程度:有次數和時間的限制 ,會被 force stop 殺死

3.添加Manifest文件屬性值爲android:persistent=“true”

代碼實現(清單文件中配置):

<application android:name="PhoneApp"
    android:persistent="true"
    android:label="@string/dialerIconLabel"
    android:icon="@drawable/ic_launcher_phone">

保活程度:一般情況下不被殺,會被 force stop 殺死
PS:該方法需要系統簽名

4.覆寫Service的onDestroy方法

思路:在onDestroy中再次啓動該服務
保活程度:很弱,只在兩種情況下work:正在運行裏殺服務、DDMS裏stop進程
代碼實現:

@Override
public void onDestroy() {
    Intent intent = new Intent(this, KeeLiveService.class);
    startService(intent);
    super.onDestroy();
}

5.監聽一堆系統靜態廣播

思路:在發生特定系統事件時,系統會發出響應的廣播,通過在 AndroidManifest 中“靜態”註冊對應的廣播監聽器,即可在發生響應事件時拉活。
保活強度:我們可以發現,這個方法都是監聽系統的一些廣播,所以我們需要在我們的應用中註冊靜態廣播,但是靜態廣播又會出現問題,那就是在4.0版本以上,沒有啓動過的應用或Force-Stop後收不到靜態廣播,也就是說4.0以後,如果我們應用從未啓動過,或者被Force-Stop殺死過,是無法接收到靜態廣播的。
  如果是兩個應用相互拉起,那麼在一個應用內可發送帶FLAG_INCLUDE_STOPPED_PACKAGES的Intent,那即使另一個應用也是以上兩種情況,也可以接收到系統的廣播。
應用1的代碼實現:

//應用1,發送拉起服務的廣播
Intent intent = new Intent();
intent.setAction("com.action.keepLive");
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
this.sendBroadcast(intent);

應用2的代碼實現:

//清單文件中配置
<receiver android:name="com.yzy.supercleanmaster.receiver.KeepLiveReceiver">
    <intent-filter>
        <action android:name="com.action.keepLive" />
    </intent-filter>
</receiver>
//源碼實現
public class KeepLiveReceiver extends BroadcastReceiver{
    //應用2中,接受應用1發送的廣播,進行服務的拉起
    @Override
    public void onReceive(Context context, Intent intent) {
        Intent i = new Intent(context, KeeLiveService.class);
        context.startService(i);
    }
}

6.監聽第三方應用的靜態廣播

思路:通過反編譯第三方 Top 應用,如:手機QQ、微信、支付寶、UC瀏覽器等,以及友盟、信鴿、個推等 SDK,找出它們外發的廣播,在應用中進行監聽,這樣當這些應用發出廣播時,就會將我們的應用拉活。
保活強度:
該方案的侷限性除與系統廣播一樣的因素外,主要受如下因素限制:
1) 反編譯分析過的第三方應用的多少;
2) 第三方應用的廣播屬於應用私有,當前版本中有效的廣播,在後續版本隨時就可能被移除或被改爲不外發,這些因素都影響了拉活的效果。

7.AlarmManager喚醒

思路:通過AlarmManager設置一個定時器,定時的喚醒服務
保活強度:killBackgroundProcess下,大部分情況work,
不敵force-stop,鬧鐘會被清除。

8.賬戶同步,定時喚醒

思路:android系統裏有一個賬戶系統,系統定期喚醒賬號更新服務,同步的事件間隔是有限制的,最短1分鐘。
難點:需要手動設置賬戶,你如何騙你的用戶給你手動設置賬戶完了之後不卸載你,必須聯網。
保活強度: 該方案適用於所有的 Android 版本,包括被 forestop 掉的進程也可以進行拉活。最新 Android 版本(Android N)中系統好像對賬戶同步這裏做了變動,該方法不再有效。

9.1像素懸浮層

思路:1像素懸浮層是傳說的QQ黑科技,監控手機鎖屏解鎖事件,在屏幕鎖屏時啓動1個像素的 Activity,在用戶解鎖時將 Activity 銷燬掉。注意該 Activity 需設計成用戶無感知。通過該方案,可以使進程的優先級在屏幕鎖屏時間由4提升爲最高優先級1。
保活強度: 前臺進程,跟前臺服務差不多。需要權限,可以被force-stop殺死。有些手機系統禁止一像素保活,如魅族手機。

10.應用間互相拉起

思路:app之間知道包名就可以相互喚醒了,比如你殺了我qq,只要微信還在就能確保隨時喚醒qq。還有百度全系app都通過bdshare實現互拉互保,自定義一個廣播,定時發,其他app收廣播自起等。

11.心跳喚醒

思路:微信保活技術,依賴系統特性:長連接網絡回包機制。
保活強度:不敵force-stop,需要網絡,API level >= 23的doze模式會關閉所有的網絡。

12.雙進程保活策略

思路:在應用被打開的時候,啓動兩個後臺服務,這兩個後臺服務是相互依存的,也就是說當一個進程被幹掉的時候,另一個存活的進程就立馬將其拉起喚醒,也就是打一個時間差。

在一個服務斷開連接的時候開啓另一個服務,示例代碼如下:

class MyServiceConnection implements ServiceConnection{

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.i(TAG, "建立連接成功!");
            
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.i(TAG, "LocalService服務被幹掉了~~~~斷開連接!");
            Toast.makeText(RemoteService.this, "斷開連接", 0).show();
            //啓動被幹掉的
            RemoteService.this.startService(new Intent(RemoteService.this, LocalService.class));
            RemoteService.this.bindService(new Intent(RemoteService.this, LocalService.class), conn, Context.BIND_IMPORTANT);
        }
        
    }

改進思路:利用JobService保證在息屏後,CPU進入休眠狀態時如果服務沒有在工作,則進行喚醒。示例代碼如下:

public class JobHandleService extends JobService{
    private final String TAG = "JobHandleService";
    private int kJobId = 0;
    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(TAG, "jobService create");
        
    }
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG, "jobService start");
        scheduleJob(getJobInfo());
        return START_NOT_STICKY;
    }
    
    @Override
    public void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();
    }
    
    @Override
    public boolean onStartJob(JobParameters params) {
        // TODO Auto-generated method stub
        Log.i(TAG, "job start");
//      scheduleJob(getJobInfo());
        boolean isLocalServiceWork = isServiceWork(this, 你的本地服務ref----XXXX.LocalService);
        boolean isRemoteServiceWork = isServiceWork(this, 你的遠程服務ref----XXXX.RemoteService);
//      Log.i(TAG, "localSericeWork:"+isLocalServiceWork);
//      Log.i(TAG, "remoteSericeWork:"+isRemoteServiceWork);
        if(!isLocalServiceWork||
           !isRemoteServiceWork){
            this.startService(new Intent(this,LocalService.class));
            this.startService(new Intent(this,RemoteService.class));
            Toast.makeText(this, "process start", Toast.LENGTH_SHORT).show();
        }
        return true;
    }

    @Override
    public boolean onStopJob(JobParameters params) {
        Log.i(TAG, "job stop");
//      Toast.makeText(this, "process stop", Toast.LENGTH_SHORT).show();
        scheduleJob(getJobInfo());
        return true;
    }

    /** Send job to the JobScheduler. */
    public void scheduleJob(JobInfo t) {
        Log.i(TAG, "Scheduling job");
        JobScheduler tm =
                (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
        tm.schedule(t);
    }
    
    public JobInfo getJobInfo(){
        JobInfo.Builder builder = new JobInfo.Builder(kJobId++, new ComponentName(this, JobHandleService.class));
        builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
        builder.setPersisted(true);
        builder.setRequiresCharging(false);
        builder.setRequiresDeviceIdle(false);
        builder.setPeriodic(10);//間隔時間--週期
        return builder.build();
    }
    
    
    /** 
     * 判斷某個服務是否正在運行的方法 
     *  
     * @param mContext 
     * @param serviceName 
     *            是包名+服務的類名(例如:net.loonggg.testbackstage.TestService) 
     * @return true代表正在運行,false代表服務沒有正在運行 
     */  
    public boolean isServiceWork(Context mContext, String serviceName) {  
        boolean isWork = false;  
        ActivityManager myAM = (ActivityManager) mContext  
                .getSystemService(Context.ACTIVITY_SERVICE);  
        List<RunningServiceInfo> myList = myAM.getRunningServices(100);  
        if (myList.size() <= 0) {  
            return false;  
        }  
        for (int i = 0; i < myList.size(); i++) {  
            String mName = myList.get(i).service.getClassName().toString();  
            if (mName.equals(serviceName)) {  
                isWork = true;  
                break;  
            }  
        }  
        return isWork;  
    }  
}

13.音樂靜音播放保活

思路:在保活服務中開啓一個MediaPlayer,循環播放靜音文件。服務被殺的時候在onDestroy中重啓服務。示例代碼如下:

public class ResidentService extends Service {
    private final static String TAG = "ResidentService";
    private MediaPlayer mMediaPlayer;

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mMediaPlayer = MediaPlayer.create(getApplicationContext(), R.raw.silent);
        if (mMediaPlayer != null) {
            mMediaPlayer.setLooping(true);
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                // 服務啓動後開始播放靜音文件
                startPlayMusic();
            }
        }).start();
        return START_STICKY;
    }

    private void startPlayMusic() {
        if (mMediaPlayer != null) {
            mMediaPlayer.start();
        }
    }

    private void pausePlayMusic() {
        if (mMediaPlayer != null) {
            mMediaPlayer.pause();
        }
    }

    private void stopPlayMusic() {
        if (mMediaPlayer != null) {
            mMediaPlayer.stop();
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // 服務被殺掉時,停止播放靜音文件,並重啓保活服務
        stopPlayMusic(); 
        Intent intent = new Intent(getApplicationContext(), GameResidentService.class);
        startService(intent);
    }
}

14.Native進程拉起

思路:開啓native子進程,定時發intent。
保活強度:單殺可以殺死,force close 5.0以上無效,5.0以下部分手機無效,第三方軟件下無效,且無法保證實時常駐。該策略建立在保證c進程不掛的基礎上,才能輪詢,但是就目前來看,只有5.0以下的非國產機纔會有這樣的漏洞。也就是說在force close的時候,系統忽略c進程的存在,5.0以上包括5.0的哪怕源生系統也會連同c進程一起清理掉,國產機就更不用說了。即使這樣,在5.0以下的非國產機上,如果安裝了獲取root權限的360\cm的話,也是可以直接清理掉,也就是說會失效。
  Native進程守護缺點非常明顯,那就是守護是單向的,也就是說只能a保b,b保不了a;a保b也不是在b死了立刻拉起來,要等到了時間纔會去拉。

15.總結

Android應用的保活策略很多,每種方式都有其優缺點和實用範圍,具體採用哪一種保活策略,需要結合應用的類型,如是系統應用還是第三方獨立應用,也要結合應用的保活需求,如什麼場景下保活、保活多久等等。有很多時候一種保活策略往往不能達到很好的保活效果,可以考慮結合幾種保活策略。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章