Android进程保活有哪些实现方案

说到进程保活,忍不住吐槽一下市场上除了微信这样的白名单大佬,其他的应用很难在后台保持长链接,保活只能是使用一些歪门邪道来延长进程的持续时间。总的来说也就这两种方法:
(1)提供进程优先级,降低进程被杀死的概率;(2)在进程被杀死后,进行拉活.
今天就先来说降低被杀死的概率方法:一像素Activity,双进程守护Service。

1像素Activity

1像素Activity的特点: 需要设置该activity的style设置透明,在手机锁屏时start;在屏幕解锁时finish,主要作用就是在App退到后台之后且锁屏的时候启动一个看不见的Activity,造成一个该App没有回退到后台的假象,从而降低被杀的几率。
代码:
在onCreate方法里设定一像素的activity

        window.setGravity(Gravity.START | Gravity.TOP);
        WindowManager.LayoutParams params = window.getAttributes();
        params.x = 0;
        params.y = 0;
        params.height = 1;
        params.width = 1;
        window.setAttributes(params);

在onReceive方法里进行操作

public void onReceive(final Context context, Intent intent) {
        Log.i("ScreenStateReceiver", "---屏幕锁屏监听---");
        if (action.equals(Intent.ACTION_SCREEN_OFF)) {
           //屏幕锁定,开启OnePixelActivity

        } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
           //屏幕解锁,finish OnePixelActivity
        }
    }

说一下详细步骤
1.需要注册一个监听屏幕锁屏和解屏的BroadcastReceiver
关于这一点有一个细节需要处理,为了放置用户快速进行锁屏和解屏的切换操作,而导致OnePixelActivity频繁开关。需要用一个Handler发送一个延迟消息处理最佳:

    private Handler mHandler;
    private boolean isScreenOn = true;
    private PendingIntent  pendingIntent;
    private List screenStateListeners = null;
    @Override
    public void onReceive(final Context context, Intent intent) {
        String action = intent.getAction();
      
        if (action.equals(Intent.ACTION_SCREEN_OFF)) {
           //标记屏幕为锁屏状态
            isScreenOn = false;
            //开启一像素的Activity
            startOnePixelActivity(context);

        } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
            //标记屏幕为解屏状态
            isScreenOn = true;     
            if(pendingIntent!=null){
                pendingIntent.cancel();
            }
     
        }
    }

  //开启一像素的Activity
    private void startOnePixelActivity(final Context context){
        if(mHandler==null){
            mHandler = new Handler(Looper.myLooper());
        }
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
              //如果屏幕此时已经打开,则不执行
                if(isScreenOn){
                    return;
                }
                if(pendingIntent!=null){
                    pendingIntent.cancel();
                }
                
                Intent startOnePixelActivity = new Intent(context, OnePixelActivity.class);
                startOnePixelActivity.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);

                //启动一像素包活activity
                pendingIntent = PendingIntent.getActivity(context, 0, startOnePixelActivity, 0);
                try {
                    pendingIntent.send();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                notifyScreenOff();
            }
        },1000);
    }

2、需要将该activity的 android:excludeFromRecents设置为true
3、需要将该Activity设置为singleInstance启动模式


为什么需要将此Activity的启动模式设置为singleInstance呢?原因是因为如果设置成其他模式,如果按照如下步骤操作的话会出现不友好的状况:
1、启动App(比如进入MainActivity),按home键让app返回后台
2、锁定屏幕,此时注册好的监听广播会启动OnePixelActivity
3、解锁屏幕,此时广播接受到此广播后会finish 掉OnePixelActivity.
也就是说,当你的app在后台的时候,你解锁屏幕,创建销毁OnePixelActvity的同时会把整个栈的activity弹出来,然会用户就会莫名其妙的看到你的app自己打开了。

另外需要注意的是,当屏幕解锁的时候,OnePixelActivity的onResume得到执行,所以在该Activity的onResume方法执行finish效果最好:

//OnePixelActivity的onResume
   protected void onResume() {
        super.onResume();
        if (DeviceUtils.isScreenOn(this)) {//如果屏幕已经打开
            finish();
        }
    }

双进程守护

所谓双进程守护原理就是很简单:两个进程的Service,相互守护;当一个进程的Service挂的时候,另一个进程的Service负责重启挂掉的Service.因为系统的进程回收机制是一个个回收的,利用这个时间差来相互唤起,当一个进程被磨灭掉,另一个马上重启,缺点是现在大部分机型只要一键清理就玩完了。文中KeepAliveService是跟我们要保活的App处于一个进程的Service,RemoteService是另外一个进程的Service;将RemoteService设置成单独的进程很方便,在AndroidMainfest.xml里面配置 android:process即可。


//将RemoteService和KeepAliveServcie处于不同的进程

守护进程其实是一个双向守护的过程,比如KeepAliveService挂了,那么RemoteService负责将KeepAliveService重启;同理,如果RemoteService挂了的话,KeepAliveService负责将RemoteService重启!那么二者是怎么知道彼此是否挂了呢?因为是位于两个进程,所以我们可以通过AIDL来确保两个不同进程的Service可以通信。
定义一个AIDL接口

interface GuardAidl {
    void notifyAlive();
}

接着我们重写KeepAliveSerivice的onBind方法,该方法返回一个IBinder给RemoteService使用:

 //将keepAliveBinder 交给RemoteService
    public IBinder onBind(Intent intent) {
        return keepAliveBinder;
    }
 
    private GuardAidl.Stub keepAliveBinder = new GuardAidl.Stub(){
        @Override
        public void notifyAlive() throws RemoteException {
            Log.i(null,"Hello RemoteService!");
        }
    };

通过KeepAliveService的onBind方法返回一个IBinder对象交给RemoteService使用,这样RemoteService就可以通过keepAliveBinder对象跟KeepAliveServcie进行通信!同样的RemoteService也是一样的代码逻辑:

public class RemoteService extends Service {
    public IBinder onBind(Intent intent) {
        return remoteBinder;
    }
    private GuardAidl.Stub remoteBinder = new GuardAidl.Stub(){
        @Override
        public void notifyAlive() throws RemoteException {
            Log.i(null,"Hello KeepAliveService!");
        }
    };
}

两个Servcie相互绑定
现在两个Service都重写onBind返回了自己的IBinder,但是怎么将自己创建的IBinder 交给对象使用呢?答案是双方调用bindSerice方法相互绑定对方,该方法是Service的一个方法,其签名如下:

bindService(Intent service, ServiceConnection conn, int flags)

所以我们绑定的时候除了传一个intent之外,还要传一个ServiceConnection。看看KeepAliveService绑定RemoteService的代码:

//KeepAliveService的onStartCommand方法
 public int onStartCommand(Intent intent, int flags, int startId) {
        Log.e("KeepAliveService", "---KeepAliveService 启动---");
        //注册锁屏广播
        registerScreenStateReceiver();
        //初始化播放器
        initMediaPlayer();
        
        //开启前台Service
        startForeground(this);
        
        //start HideNotifactionService
        startHideNotificationService();
        
        //绑定守护进程
        bindRemoteService();
        
        return START_STICKY;
    }
    
   //绑定守护进程
   private void bindRemoteService(){
        Intent intent = new Intent(this,RemoteService.class);
        bindService(intent,connection,Context.BIND_ABOVE_CLIENT);
    }

   private ServiceConnection connection = new ServiceConnection() {
       //调用此方法说明RemoteServcie已经挂掉,需要重新启动
        public void onServiceDisconnected(ComponentName name) {
         Intent remoteService = new Intent(KeepAliveService.this,
                        RemoteService.class);
                KeepAliveService.this.startService(remoteService);
                
            Intent intent = new Intent(KeepAliveService.this, RemoteService.class);
            //将KeepAliveService和RemoteService进行绑定
            KeepAliveService.this.bindService(intent, connection,
                    Context.BIND_ABOVE_CLIENT);
        }
        
        @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
            try {
                //与RemoteService绑定成功
                GuardAidl remoteBinder = GuardAidl.Stub.asInterface(service);
                remoteBinder.notifyAlive();
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    };

其中ServiceConnection有两个方法onServiceDisconnected和onServiceConnected,当RemoteService被异常销毁挂掉的时候,onServiceDisconnected会被调用。此时需要在该方法里面重新启动RemoteService;而onServiceConnected方法则是系统调用它来传送在RemoteService的onBind()中返回的IBinder!同理,RemoteService绑定KeepAlivce的代码跟上面雷同,再此就不在贴出来了,
源码可以点击此处查阅

你可能感兴趣的:(Android进程保活有哪些实现方案)