进程保活的关键点有两个,一个是进程优先级的理解,优先级越高存活几率越大。二是弄清楚哪些场景会导致进程会kill,然后采取下面的策略对各种场景进行优化:
- 提高进程的优先级
- 在进程被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,双进程守护
两个进程共同运行,如果有其中一个进程被杀,那么另外一个进程就会将被杀的进程重新拉起
参考代码:
网友评论