进程保活

2019-12-18  本文已影响0人  Coder_Sven

进程保活的关键点有两个,一个是进程优先级的理解,优先级越高存活几率越大。二是弄清楚哪些场景会导致进程会kill,然后采取下面的策略对各种场景进行优化:

  1. 提高进程的优先级
  2. 在进程被kill之后能够唤醒

进程优先级

Android一般的进程优先级划分:
​ 1.前台进程 (Foreground process)
​ 2.可见进程 (Visible process)
​ 3.服务进程 (Service process)
​ 4.后台进程 (Background process)
​ 5.空进程 (Empty process)
​ 这是一种粗略的划分,进程其实有一种具体的数值,称作oom_adj,注意:数值越大优先级越低:

1,Activity提权

原理:监控手机锁屏解锁事件,在屏幕锁屏时启动1个像素透明的 Activity,在用户解锁时将 Activity 销毁掉,从而达到提高进程优先级的作用。

造一个一个像素的Activity

public class KeepActivity extends Activity {
    private static final String TAG = "KeepActivity";
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.e(TAG,"启动Keep");
        Window window = getWindow();
        //设置这个activity在左上角
        window.setGravity(Gravity.START | Gravity.TOP);
        WindowManager.LayoutParams attributes = window.getAttributes();
        //宽高为1
        attributes.width = 1;
        attributes.height = 1;
        //起始位置左上角
        attributes.x = 0;
        attributes.y = 0;
        window.setAttributes(attributes);

        KeepManager.getInstance().setKeepActivity(this);
    }
}

监听系统开屏或者息屏的广播

public class KeepReceiver extends BroadcastReceiver {
    private static final String TAG = "KeepReceiver";
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        Log.e(TAG, "receive:" + action);
        if (TextUtils.equals(action, Intent.ACTION_SCREEN_OFF)) {
            //灭屏 开启1px activity
            KeepManager.getInstance().startKeep(context);
        } else if (TextUtils.equals(action, Intent.ACTION_SCREEN_ON)) {
            //亮屏 关闭
            KeepManager.getInstance().finishKeep();
        }
    }
}
public class KeepManager {
    private static final KeepManager ourInstance = new KeepManager();

    public static KeepManager getInstance() {
        return ourInstance;
    }

    private KeepManager() {
    }
    private KeepReceiver keepReceiver;
    private WeakReference<Activity> mKeepActivity;
    /**
     * 注册
     * @param context
     */
    public void registerKeepReceiver(Context context){
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_SCREEN_OFF);
        filter.addAction(Intent.ACTION_SCREEN_ON);
        keepReceiver = new KeepReceiver();
        context.registerReceiver(keepReceiver, filter);
    }

    /**
     * 反注册
     * @param context
     */
    public void unRegisterKeepReceiver(Context context){
        if (null != keepReceiver) {
            context.unregisterReceiver(keepReceiver);
        }
    }

    /**
     * 启动1个像素的KeepActivity
     * @param context
     */
    public void startKeep(Context context) {
        Intent intent = new Intent(context, KeepActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(intent);
    }

    /**
     * finish1个像素的KeepActivity
     */
    public void finishKeep() {
        if (null != mKeepActivity) {
            Activity activity = mKeepActivity.get();
            if (null != activity) {
                activity.finish();
            }
            mKeepActivity = null;
        }
    }

    public void setKeepActivity(KeepActivity mKeepActivity) {
        this.mKeepActivity = new WeakReference<Activity>(mKeepActivity);
    }
}

2,Service提权

创建一个前台服务用于提高app在按下home键之后的进程优先级

startForeground(ID,Notification):使Service成为前台Service。 前台服务需要在通知栏显示一条通知

public class ForegroundService extends Service {
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel("sven", "sven",
                    NotificationManager.IMPORTANCE_LOW);
            NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
            if (manager == null)
                return;
            manager.createNotificationChannel(channel);

            Notification notification = new NotificationCompat.Builder(this, "sven").setAutoCancel(true).setCategory(
                    Notification.CATEGORY_SERVICE).setOngoing(true).setPriority(
                    NotificationManager.IMPORTANCE_LOW).build();
            startForeground(10, notification);
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            //如果 18 以上的设备 启动一个Service startForeground给相同的id
            //然后结束那个Service
            startForeground(10, new Notification());
            startService(new Intent(this, InnnerService.class));
        } else {
            startForeground(10, new Notification());
        }
    }

    public static class InnnerService extends Service {

        @Override
        public void onCreate() {
            super.onCreate();

            //发送相同的Notification,然后将其取消并取消自己的前台显示
            Notification.Builder builder = new Notification.Builder(this);
            startForeground(10, new Notification());
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    stopForeground(true);
                    NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
                    manager.cancel(10);
                    stopSelf();
                }
            },100);
        }

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

3,广播拉活

在发生特定系统事件时,系统会发出广播,通过在 AndroidManifest 中静态注册对应的广播监听器,即可在发生响应事件时拉活。但是从android 7.0开始,对广播进行了限制,而且在8.0更加严格

4,全家桶拉活

有多个app在用户设备上安装,只要开启其中一个就可以将其他的app也拉活。比如手机里装了手Q、QQ空间、兴趣部落等等,那么打开任意一个app后,其他的app也都会被唤醒。

5,Service机制(Sticky)拉活

将 Service 设置为 START_STICKY,利用系统机制在 Service 挂掉后自动拉活

只要 targetSdkVersion 不小于5,就默认是 START_STICKY。
但是某些ROM 系统不会拉活。并且经过测试,Service 第一次被异常杀死后很快被重启,第二次会比第一次慢,第三次又会比前一次慢,一旦在短时间内 Service 被杀死4-5次,则系统不再拉起。

6,JobScheduler拉活

JobScheduler允许在特定状态与特定时间间隔周期执行任务。可以利用它的这个特点完成保活的功能,效果即开启一个定时器,与普通定时器不同的是其调度由系统完成。

@SuppressLint("NewApi")
public class MyJobService 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(),
                        MyJobService.class.getName())).setPersisted(true);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
            //7.0以上延迟1s执行
            builder.setMinimumLatency(1000);
        }else{
            //每隔1s执行一次job
            builder.setPeriodic(1000);
        }
        jobScheduler.schedule(builder.build());
    }

    @Override
    public boolean onStartJob(JobParameters jobParameters) {
        Log.e(TAG,"开启job");
        //7.0以上轮询
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
            startJob(this);
        }

        return false;
    }

    @Override
    public boolean onStopJob(JobParameters jobParameters) {
        return false;
    }
}

7,推送拉活

根据终端不同,在小米手机(包括 MIUI)接入小米推送、华为手机接入华为推送。

8,Native拉活

Native fork子进程用于观察当前app主进程的存亡状态。对于5.0以上成功率极低。

9,双进程守护

两个进程共同运行,如果有其中一个进程被杀,那么另外一个进程就会将被杀的进程重新拉起

参考代码:

[https://github.com/games2sven/ProcessLiving]:

上一篇下一篇

猜你喜欢

热点阅读