守护进程

守护进程: 一直在后台运行的进程。本文主要讲解一些android比较常用的守护进程的方法。

实现思想:
1.保活,通过提高进程优先级,降低进程被杀死的概率
2.拉起,进程被杀死后,进行拉起

相关基础知识

Android进程优先级

在Android中,进程粗略的分成五个等级,分别是:

1.前台进程
2.可见进程
3.服务进程
4.后台进程
5.空进程

此类相关知识可以在https://developer.android.com/guide/components/processes-and-threads 查询阅读

前台进程:

用户当前操作所必须的进程。如果一个进程满足以下任一条件,即可视为前台进程:
托管用户正在交互的 Activity(已调用 Activity 的 onResume() 方法)
托管某个 Service,后者绑定到用户正在交互的 Activity
托管正在“前台”运行的 Service(服务已调用 startForeground())
托管正执行一个生命周期回调的 Service(onCreate()、onStart() 或 onDestroy())
托管正执行其 onReceive() 方法的 BroadcastReceiver
通常,只有在内存不足以支持它们同时继续运行这一万不得已的情况下,系统才会终止它们。

可见进程:

没有任何前台组件、但仍会影响用户在屏幕上所见内容的进程。 如果一个进程满足以下任一条件,即视为可见进程:
托管不在前台、但仍对用户可见的 Activity(已调用其 onPause() 方法)。例如,如果前台 Activity 启动了一个对话框,允许在其后显示上一 Activity,则有可能会发生这种情况。(除了对话框可以是透明的Activity)
托管绑定到可见(或前台)Activity 的 Service。
可见进程被视为是极其重要的进程,除非为了维持所有前台进程同时运行而必须终止,否则系统不会终止这些进程

后台进程:

包含目前对用户不可见的 Activity 的进程(已调用 Activity 的 onStop() 方法)。这些进程对用户体验没有直接影响,系统可能随时终止它们,以回收内存供前台进程、可见进程或服务进程使用。 通常会有很多后台进程在运行,因此它们会保存在 LRU (最近最少使用)列表中,以确保包含用户最近查看的 Activity 的进程最后一个被终止。如果某个 Activity 正确实现了生命周期方法,并保存了其当前状态,则终止其进程不会对用户体验产生明显影响,因为当用户导航回该 Activity 时,Activity 会恢复其所有可见状态

空进程:

不含任何活动应用组件的进程。保留这种进程的的唯一目的是用作缓存,以缩短下次在其中运行组件所需的启动时间。 为使总体系统资源在进程缓存和底层内核缓存之间保持平衡,系统往往会终止这些进程。

Android进程优先级的依据

1.每一个Android应用进程中,都可能包含四大组件中的一个或多个
应用进程是由AMS发送请求让zygote创建的,并由AMS对于每一个运行的都有一个ProcessRecord对象与之对应。组件的状态就是其所在进程优先级的决定性因素。组件的状态指:
Activity是否在前台,用户是否可见
Service正在被哪些客户端使用
ContentProvider正在被哪些客户端使用, BroadcastReceiver是否在接受广播

常见的使用方案

1.白色保活
2.灰色保活
3.黑色保活
4.双进程守护
5.JobService轮询

白色保活

所谓白色保活,就是通过启动前台服务使得进程优先级提高到前台进程。
优点:写法简单,处理方便
缺点:前台服务和通知绑定在一起,意味着开启服务要伴随一条通知在通知栏,用户有感知 。
API:
startForeground(int id, Notification notification);
target26,并且系统8.0之后由于谷歌对后台服务的限制,做法改为:
startForegroundService(context, intent);
并且在创建服务后的五秒内调用
startForeground(0, new Notification());
不调用会发生anr

灰色保活

所谓灰色保活,就是利用系统漏洞开启前台服务。
优点:开启前台服务的情况下,可以去掉通知,使得用户无感知
缺点:target26 8.0以上的系统该漏洞已修复,因此不适用
做法:
正常开启一个服务并发出通知后,开启另一个服务也发出通知,保持两条通知的id一致,关掉第二个服务并且在onDestroy中调用stopForeground(true)去掉通知。此时第一个服务仍为前台服务。

黑色保活

所谓黑色保活,就是利用通知拉起进程。
适用对象:腾讯系全家桶,阿里系全家桶,应用之间互相拉起

双进程守护

所谓双进程守护,就是指两个进程互相监视,一旦有一个进程死了,另一个进程监听到就拉起。
依托这个原理,衍生出的双进程守护的方案有多种,比如利用监听socket连接中断实现,利用文件锁实现,利用android的绑定服务实现。以服务绑定为例来说:
context.bindService(intent, serviceconnection, flag);
这里的serviceconnection就是监听回调,回调中有onServiceConnected方法和onServiceDisconnected方法这两个,通过onServiceDisconnected可以监听到另一个服务是否还存活。把两个服务放在两个进程就能够做到监听拉起进程。

JobService

通过定时触发任务,判定进程是否存活,如果不存活了,则拉起
优点:5.0以后出现的JobService是官方推荐的方式,比较稳定
缺点:触发时机不够实时,JobService的触发时机会是充电时,闲暇时等特殊时机或者是周期性执行

Demo举例:

将灰色保活,双进程守护,JobService融合在一起使用。

LocalService(写应用逻辑的代码)
public class LocalService extends Service {    private final static int NOTIFICATION_ID = 1003;    private static final String TAG = "LocalService";    private ServiceConnection serviceConnection;    @Override    public IBinder onBind(Intent intent) {        return new MyBinder();    }    @Override    public void onCreate() {        super.onCreate();        //此处可以写上一些业务逻辑            try {            Notification notification = new Notification();            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {                startForeground(NOTIFICATION_ID, notification);            } else {                startForeground(NOTIFICATION_ID, notification);                // start InnerService                startService(new Intent(this, InnerService.class));            }        } catch (Throwable e) {            e.printStackTrace();        }        serviceConnection = new LocalServiceConnection();        startService(new Intent(this, RemoteService.class));        bindService(new Intent(this, RemoteService.class), serviceConnection, BIND_AUTO_CREATE);    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        return super.onStartCommand(intent, flags, startId);    }    class LocalServiceConnection implements ServiceConnection {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            Log.d(TAG, "bind RemoteService");            //服务连接后回调        }        @Override        public void onServiceDisconnected(ComponentName name) {            Log.e(TAG, "remote service died,make it alive");            //连接中断后回调            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();            try {                startForeground(NOTIFICATION_ID, new Notification());            } catch (Throwable throwable) {                throwable.printStackTrace();            }            stopSelf();        }        @Override        public void onDestroy() {            stopForeground(true);            super.onDestroy();        }    }    static class MyBinder extends IMyAidlInterface.Stub {        @Override        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {        }    }    @Override    public void onDestroy() {        super.onDestroy();    }}

从代码中可以看出这里使用了灰色保活,开启了另一个服务,使用同一通知id,之后又将该服务关闭,从而去掉了通知栏的通知,注意该方法在target26 8.0的手机上已无效,target26 8.0中官方修复了该漏洞,所以只能乖乖的弹出通知保持前台服务。

RemoteService:
public class RemoteService extends Service {    private final static int NOTIFICATION_ID = 1002;    private static final String TAG = "RemoteService";    private ServiceConnection serviceConnection;    @Override    public IBinder onBind(Intent intent) {        return new MyBinder();    }    @Override    public void onCreate() {        super.onCreate();        try {            Notification notification = new Notification();            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {                startForeground(NOTIFICATION_ID, notification);            } else {                startForeground(NOTIFICATION_ID, notification);                // start InnerService                startService(new Intent(this, InnerService.class));            }        } catch (Throwable e) {            e.printStackTrace();        }        serviceConnection = new RemoteServiceConnection();        startService(new Intent(this, LocalService.class));        bindService(new Intent(this, LocalService.class), serviceConnection, BIND_AUTO_CREATE);    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        return super.onStartCommand(intent, flags, startId);    }    class RemoteServiceConnection implements ServiceConnection {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            //服务连接后回调            Log.d(TAG, "bind LocalService");        }        @Override        public void onServiceDisconnected(ComponentName name) {            Log.e(TAG, "main process local service died,make it alive");            //连接中断后回调            startService(new Intent(RemoteService.this, LocalService.class));            bindService(new Intent(RemoteService.this, LocalService.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();            try {                startForeground(NOTIFICATION_ID, new Notification());            } catch (Throwable throwable) {                throwable.printStackTrace();            }            stopSelf();        }        @Override        public void onDestroy() {            stopForeground(true);            super.onDestroy();        }    }    static class MyBinder extends IMyAidlInterface.Stub {        @Override        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {        }    }}
DaemonJobService:
public class DaemonJobService extends JobService {    private static final String TAG = "MyJobService";    public static void startJob(Context context) {        JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);        JobInfo.Builder builder = new JobInfo.Builder(10, new ComponentName(context.getPackageName(), DaemonJobService.class.getName())).setPersisted(true);        //小于7.0        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {            // 每隔1s 执行一次 job            builder.setPeriodic(1_000);        } else {            //延迟执行任务            builder.setMinimumLatency(1_000);        }        if (jobScheduler != null) {            jobScheduler.schedule(builder.build());        }    }    @Override    public boolean onStartJob(JobParameters params) {        //如果7.0以上 轮训        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {            startJob(this);        }        //JobSchedule结合双进程守护        boolean isLocalRun = ProcessUtils.isRunningService(this, LocalService.class.getName());        boolean isRemoteRun = ProcessUtils.isRunningService(this, RemoteService.class.getName());        if (!isLocalRun || !isRemoteRun) {            startService(new Intent(this, LocalService.class));            startService(new Intent(this, RemoteService.class));        }        return false;    }    @Override    public boolean onStopJob(JobParameters params) {        return false;    }}
使用方法
public class DaemonApp extends BaseApp {    @Override    public void onCreate() {        super.onCreate();        //守护进程初始化        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT_WATCH){            DaemonJobService.startJob(this);        } else {            startService(new Intent(this, LocalService.class));            startService(new Intent(this, RemoteService.class));        }    }}

使用adb命令查看效果

adb shell dumpsys activity services 包名
可以查看某个应用对应的服务的情况

图中createdFromFg代表该服务是前台服务,而从自身进程名和绑定的进程名称可以看出这两个服务在两个进程,并且相互绑定了,即双进程守护构建完成

在Application中进行初始化即可
Demo下载链接:https://download.csdn.net/download/breeze048/10771453

更多相关文章

  1. Android模块化(二)——模块通信和模块间服务调用
  2. Android之消息推送实现
  3. Android启动过程分析(1)
  4. Android推送通知指南
  5. 从Android(安卓)8.0源码的角度剖析Android系统启动过程(1)
  6. Android访问WCF服务(上篇)-服务端开发
  7. Android(安卓)根文件系统启动过程(init进程 详细分析)
  8. 分析Android(安卓)根文件系统启动过程(init守护进程分析)
  9. Android的启动过程分析(从进程和Framework的角度)-android学习之

随机推荐

  1. php7 错误处理机制修改实例分析
  2. php7 list()、session及其他模块的修改实
  3. php中get_object_vars()在数组的实例用法
  4. jsp使用sessionScope获取session案例详解
  5. 无法更新apt镜像源?树莓派安装最新版Debia
  6. 漫画线稿怎么画?初学者必备线稿教程
  7. PHP解密支付宝小程序的加密数据、手机号
  8. php自动加载代码实例详解
  9. 【北亚服务器数据恢复】华为OceanStor系
  10. 盒模型,多媒体查询实例,rem和em的区别