进程优先级:
Android一般的进程优先级划分:
1.前台进程 (Foreground process) :onResume的Activity,前台Service
2.可见进程 (Visible process):onPause的Activity
3.服务进程 (Service process)
4.后台进程 (Background process)
5.空进程 (Empty process)
进程被杀的场景:
1.点击home键使app长时间停留在后台,内存不足被kill
可以通过运行一个前台Service将进程的优先级从4提升到1,缺点是通知栏会有一条通知消息
2.进入锁屏状态一段时间,省电机制会kill后台进程
注册广播监听锁屏和解锁事件, 锁屏后启动一个1像素的透明Activity,提升进程优先级,解锁后销毁这个Activity
adb查看进程优先级:
首先进入adb shell,然后通过ps命令查询进程的pid,再用pid查询对应的优先级:
adb shell //进入adb shell
ps //查询进程的pid
cat /proc/&pid/oom_adj //查询pid进程的优先级
这里查询优先级会得到一个数字,数字越小表明优先级越高
一般系统应用是一个负值,前台应用优先级是0,后台应用是一个正整数
进程保活方案:
方案1:息屏时通过广播打开一个1像素的Activity:
public class ProxyActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Window window = getWindow();
window.setGravity(Gravity.START | Gravity.TOP);
WindowManager.LayoutParams attributes = window.getAttributes();
attributes.width = 1;
attributes.height = 1;
attributes.x = 0;
attributes.y = 0;
window.setAttributes(attributes);
}
public static void launch(Activity activity) {
Intent intent = new Intent(activity, ProxyActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
activity.startActivity(intent);
}
}
监听屏幕的广播:
private class ScreenBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (Intent.ACTION_SCREEN_ON.equals(action)) { // 开屏
ProxyActivity.launch(activity)
} else if (Intent.ACTION_SCREEN_OFF.equals(action)) { // 锁屏
ProxyActivity.close();
}
}
}
注册广播:
ScreenBroadcastReceiver broadcast = new ScreenBroadcastReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
registerReceiver(broadcast, intentFilter);
用adb查询进程优先级测试一下效果:
正常打开App,使app位于前台,或者当App位于前台时熄灭屏幕,查询优先级:
zerofltechn:/ $ cat /proc/32333/oom_adj
0
使App退到后台,或者退到后台再锁屏之后查询优先级,会得到:
zerofltechn:/ $ cat /proc/32333/oom_adj
11
采用息屏后开启Activity的方案后,将App退到后台再息屏打印该进程的优先级:
zerofltechn:/ $ cat /proc/4239/oom_adj
0
可见此方案可以在息屏情况下提升进程的优先级
方案2:用没有notification的前台Service提升App优先级
对于 API level < 18 :调用startForeground(ID, new Notification()),发送空的Notification ,图标则不会显示。
对于 API level >= 18:在需要提优先级的service A启动一个InnerService,两个服务同时startForeground,且绑定同样的 ID。Stop 掉InnerService ,这样通知栏图标即被移除。
例:
public class KeeAliveService extends Service {
public static final int NOTIFICATION_ID = 0x11;
@Override
public void onCreate() {
super.onCreate();
if (Build.VERSION.SDK_INT < 18) {
//Android4.3以下 ,此方法能有效隐藏Notification上的图标
startForeground(NOTIFICATION_ID, new Notification());
} else if (Build.VERSION.SDK_INT >= 18 && Build.VERSION.SDK_INT < 25){
//Android4.3 - Android7.0,此方法能有效隐藏Notification上的图标
Notification.Builder builder = new Notification.Builder(this);
builder.setSmallIcon(R.mipmap.ic_launcher);
startForeground(NOTIFICATION_ID, builder.build());
//这里开启了另外一个前台Service,并在一段时间后杀死自己,这样可以使得KeeAliveService没有通知
startService(new Intent(this, InnerService.class));
} else{
//Android7.1 必须显示一条通知
service.startForeground(GRAY_SERVICE_ID, new Notification());
}
}
@Override
public IBinder onBind(Intent intent) {
return binder;
}
public static class InnerService extends Service {
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
//发送与KeepLiveService中ID相同的Notification,然后将其取消并取消自己的前台显示
Notification.Builder builder = new Notification.Builder(this);
builder.setSmallIcon(R.mipmap.ic_launcher);
startForeground(NOTIFICATION_ID, builder.build());
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
stopForeground(true);
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
manager.cancel(NOTIFICATION_ID);
stopSelf();
}
}, 1000);
}
}
}
只要在App启动后开启KeeAliveService服务,App的优先级就会提高
基于Android7.0测试一下效果:
首先未开启这个服务时将App退到后台,或退到后台后息屏:
zerofltechn:/ $ cat /proc/9156/oom_adj
11
开启KeeAliveService,将应用退到后台,或者退到后台后息屏:
zerofltechn:/ $ cat /proc/9156/oom_adj
3
可见使用前台服务可以使App在后台时提升优先级