史上最全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 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. 关键代码
//反编译支付宝后,发现支付包有如下拉活操作

            
                
                
                
                
                
            
        

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中配置不同的进程

//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/

你可能感兴趣的:(android)