天天看點

史上最全android保活方案及比較分析方案對比

方案對比

方案名稱 方案簡述 優點 缺點 适用情況
1像素保活 在螢幕關閉時打開一個1px的activity,螢幕亮時關閉此activity 易于實作 鎖屏時才能提高優先級,不穩定 适用于搭配其他方案一起使用
前台服務保活 啟動一個前台服務,提高應用的優先級 系統機制 增加備援服務 适用于常用保活
廣播拉活 在接收到特定廣播時拉起應用 易于實作 小廠使用,不夠穩定 可作為輔助方案
sticky拉活 利用service的粘性來拉起應用 系統喚醒,方式文明 應用被殺死4-5次後系統不再拉起應用 此方式效果不明顯,不推薦
賬戶同步拉活 利用賬戶同步機制拉活 系統喚醒,比較穩定 時間不能把控 适用于間隔性拉活需求,應用不需要持續存在
JobScheduler拉活 定時任務拉起應用 保活穩定 非常消耗性能 适用于流氓應用
雙程序保活 兩個程序互相拉起來保活 穩定 增加系統開銷 适用于常用保活

注:需要注意的是上述保活指的是僅能提高程序優先級,系統不會自動殺死,但是如果使用者主動殺死,應用便死了,拉活指的是被使用者殺死仍然可以再拉起來,在低版本和部分廠商機型上适用,但在部分廠商和高版本安卓系統中,清理時會全部殺死,除非加入系統白名單,但上述方式仍可以使用來提高程序優先級,使系統自動清理時不殺死我們的應用

代碼

github位址:https://github.com/dingjiaxing/KeepAliveDemo/

方案詳細說明

1像素保活

  1. 描述

    在螢幕關閉時打開一個1px的activity,螢幕亮時關閉此activity,因為在螢幕關閉時activity處于前台,是以系統将會把我們應用的優先級提高,進而遇到記憶體達到門檻值時便不會殺死我們應用

  2. 關鍵代碼
//1像素的activity,主題設為透明
public class OnePixelActivity extends Activity{
    private static final String TAG = "OnePixelActivity";
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Window window=getWindow();
        window.setGravity(Gravity.START|Gravity.TOP);
        WindowManager.LayoutParams params=window.getAttributes();
        params.width = 1;
        params.height = 1;
        params.x = 0;
        params.y = 0;
        window.setAttributes(params);
        KeepManager.getInstance().setOnePixelActivity(this);
    }
}
//廣播接受者,接受廣播
public class OnePixelKeepReceiver extends BroadcastReceiver {
    private static final String TAG = "OnePixelKeepReceiver";
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if(action!=null){
            if(action.equals(Intent.ACTION_SCREEN_OFF)){
                //螢幕關閉,打開1像素activity
                KeepManager.getInstance().startOnePixel(context);
            }else if(action.equals(Intent.ACTION_SCREEN_ON)){
                //螢幕打開,關閉1像素activity
                KeepManager.getInstance().finishOnePixel();
            }
        }
    }
}
           

前台服務保活

  1. 描述

    啟動一個前台服務,提高應用的優先級,進而達到保活的目的

  2. 關鍵代碼
//第一個activity中啟動前台service
startService(new Intent(this,ForegroundService.class));
//ForegroundService.class
public class ForegroundService extends Service {
    private static final String TAG = "ForegroundService";
    private static int SERVICE_ID=137890;
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG,"ForegroundService onCreate");
        if(Build.VERSION.SDK_INT<Build.VERSION_CODES.JELLY_BEAN_MR2){
            //4.3以下
            startForeground(SERVICE_ID,new Notification());
        }else if(Build.VERSION.SDK_INT<Build.VERSION_CODES.O){
            //4.3 -> 7.0,7.0以前,先啟動一個notification,再啟動一個相同id的service,再關閉該service,系統會認定該notification也關閉,8.0以上解決了此bug
            startForeground(SERVICE_ID,new Notification());
            startService(new Intent(this,InnerService.class));
        }else {
            //8.0以上,系統會提示“程序正在運作中”
            NotificationManager manager= (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
            NotificationChannel channel=new NotificationChannel("channel","xx",NotificationManager.IMPORTANCE_NONE);
            if(manager==null){
                manager.createNotificationChannel(channel);
                Notification notification=new NotificationCompat.Builder(this,"channel").build();
                startForeground(SERVICE_ID,notification);
            }
        }
    }
    public static class InnerService extends Service{
        @Override
        public void onCreate() {
            super.onCreate();
            Log.d(TAG,"InnerService onCreate");
            startForeground(SERVICE_ID,new Notification());
            stopSelf();
        }
    }
           

廣播拉活

  1. 描述

    在接收到特定廣播時拉起應用

  2. 注意
  • 可參考注冊的系統廣播:開機廣播、電量變化廣播、螢幕開閉廣播等
  • 在部分廠商中,一些系統廣播并未發出去
  • 這個方案對于微信、阿裡等大廠來說非常适用,比如隻要微信在,便可以拉起微信系所有應用,但從張小龍的性格來看應該不會這麼做
  • 對于小廠來說,我們可以反編譯大廠app的包,進而拿到對應的廣播,我們也注冊一下該廣播就好了
  1. 關鍵代碼
//反編譯支付寶後,發現支付包有如下拉活操作
<receiver android:enabled="false" android:name="com.alipay.mobile.quinox.preload.PreloadReceiver">
            <intent-filter android:priority="2147483647">
                <action android:name="android.intent.action.BOOT_COMPLETED"/>
                <action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
                <action android:name="android.intent.action.USER_PRESENT"/>
                <action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
                <action android:name="android.intent.action.MY_PACKAGE_REPLACED"/>
            </intent-filter>
        </receiver>
           

sticky拉活

  1. 描述

    利用service的粘性來拉起應用,onStartCommand中傳回START_STICKY,如果service被殺死,系統将會再次打開該service

  2. 關鍵代碼
public class StickyService extends Service {
    private static final String TAG = "StickyService";
    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG,"onCreate");
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG,"onStartCommand");
        //傳回START_STICKY時,如果該service被關閉,系統會重新再打開該service,如果使用者連續殺死四五次,系統便不會再打開該service
        return START_STICKY;
//        return super.onStartCommand(intent,flags,startId);
    }
}
           

賬戶同步拉活

  1. 描述

    利用賬戶同步機制拉活,系統大概每15分鐘左右會同步一次賬戶,這個時候會拉起我們的應用,并且目前很多應用都會這麼做

  2. 關鍵代碼
//添加賬戶
public static void addAccount(Context context){
        AccountManager accountManager=(AccountManager)context.getSystemService(Context.ACCOUNT_SERVICE);
        Account[] accounts=accountManager.getAccountsByType(ACCOUNT_TYPE);
        if(accounts.length>0){
            Log.d(TAG,"賬戶已存在");
            return;
        }
        Account account=new Account("xx",ACCOUNT_TYPE);
        accountManager.addAccountExplicitly(account,"xx",new Bundle());
    }
//設定自動同步
    public static void autoSync(){
        Account account=new Account("xx",ACCOUNT_TYPE);
        ContentResolver.setIsSyncable(account,"com.xx.provider",1);
        ContentResolver.setSyncAutomatically(account,"com.xx.daemon.provider",true);
        ContentResolver.addPeriodicSync(account,"com.xx.daemon.provider",new Bundle(),1);
    }
           

JobScheduler拉活

  1. 描述

    JobScheduler是系統為我們提供的定時任務的類,我們可以讓該定時任務持續運作,進而達到拉活的目的,因為是在持續運作,是以對性能消耗非常高,但拉活效果很穩定

  2. 關鍵代碼
public static void startJob(Context context){
        JobScheduler jobScheduler= (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
        JobInfo.Builder builder=new JobInfo.Builder(8,new ComponentName(context.getPackageName(),MyJobService.class.getName())
                ).setPersisted(true);
        //小于 7.0
        if(Build.VERSION.SDK_INT < Build.VERSION_CODES.N){
            //每隔 1s 執行一次 job
            builder.setPeriodic(1000);
        }else {
            //延遲執行任務
            builder.setMinimumLatency(1000);
        }
        jobScheduler.schedule(builder.build());
    }
    @Override
    public boolean onStartJob(JobParameters params) {
        Log.d(TAG,"onStartJob");
        //7.0以上輪訓
        if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N){
            startJob(this);
        }
        return false;
    }
           

雙程序保活

  1. 描述

    建立兩個service:LocalService和RemoteService,LocalService屬于目前程序,RemoteService屬于單獨的另外一個程序,如果在service連接配接斷開時另外一個service中會觸發onServiceDisconnected,此時再啟動service

  2. 關鍵代碼
//manifest中配置不同的程序
<service android:name=".service.RemoteService"
            android:exported="true"
            android:process=":remote"
            />
//LocalService.class,RemoteService與此類似
public class LocalService extends Service {
    private static final String TAG = "LocalService";
    ServiceConnection serviceConnection;
    MyBinder binder;
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }
    @Override
    public void onCreate() {
        super.onCreate();
        if(binder==null){
            binder=new MyBinder();
        }
        serviceConnection=new MyServiceConnection();
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        bindService(new Intent(LocalService.this,RemoteService.class),
                serviceConnection,BIND_AUTO_CREATE);
        return START_STICKY;
    }
    class MyBinder extends IRemoteConnection.Stub{
        @Override
        public String getProcessName() throws RemoteException {
            return "LocalService";
        }
    }
    class MyServiceConnection implements ServiceConnection{
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.d(TAG,"remote service 可能被殺死了,拉活");

            startService(new Intent(LocalService.this,RemoteService.class));
            bindService(new Intent(LocalService.this,RemoteService.class),
                    serviceConnection,BIND_AUTO_CREATE);
        }
    }

    public static class InnerService extends Service{
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
        @Override
        public void onCreate() {
            super.onCreate();
            startForeground(16,new Notification());
            stopSelf();
        }
    }
}

           

相關保活文章

  1. 微信團隊原創分享:https://blog.csdn.net/guojin08/article/details/79623311
  2. 解讀Android程序優先級ADJ算法:http://gityuan.com/2018/05/19/android-process-adj/