Android进程与进程保活(涉及Notification)
由于内存限制,android系统会在内存不足时回收进程,
* 一、进程回收顺序:*
优先级:前台进程>可视进程>服务进程>后台进程>内容供应根节点>空进程
oom_adj越大 越可能被回收
系统进程 <0 前台进程0 可见进程1
进程级别参考:https://juejin.im/entry/58acf391ac502e007e9a0a11
1、Foreground process 前台进程
下面几种情况属于前台进程:
(1)Activity正在与用户进程交互(Activity的onResume已经被调用)
(2)与正在和用户交互的Activity绑定的Service
(3)Service运行在前台——Service中调用了startForeground函数
(4)Service正在执行生命周期回调函数(onCreate,onStart,onDestory)
(5)BroadcastReceiver正在执行onReceive方法
2、Visible process 可视进程
下面几种情况属于可视进程:
(1)Activity没有运行在前台,但是用户仍然可见(它的onPause方法被调用),例如:当前台Activity启动了一个Dialog,这样Dialog运行在前台,Activity仍然可见,属于可视进程。
(2)与一个可视的Activity绑定的服务所在的进程
3、Service process 服务进程
运行服务的进程被startService()启动,并且没有进入上面1中(3)、(4)这两种情况。例如,音乐播放、网络下载数据
4、Background process 后台进程
当Activity不可见的时候,它的进程属于后台进程(Activity的onStop方法被调用)
5、Empty process 空进程
没有包含活动应用组件的进程为空进程,也就是进程的应用组件已经运行完毕。
查看某个包的进程
Terminal 或者 cmd 进入 adb shell 模式
ps|grep 包名
第一个参数:u0_a124 当前用户
第二个参数:进程ID
第三个参数: 进程的父进程ID
第四个参数:进程的虚拟内存大小
第五个参数:实际内存大小
最后一个参数:进程名
查看某个进程的进程优先级(oom_adj)
cat /proc/进程id/oom_adj
(注意 cat后要加空格)
permission denied
要root
检查Service是否开启在前台
1 用 cat /proc/进程名/包名
查看开启前后的进程优先级变化 优先级降低了并且为0即为前台进程
2 或者
dumpsys activity services PackageName
查看 services 找到相应的services名
发现 isForeground=true 即为前台进程
进程保活常见方式
一、白色手段
开启前台Service,会在通知栏显示
通过notification方式 如音乐播放
如果希望从前台移除这个服务,只需要调用stopForeground(),一般情况我们只需要在onStartCommand里面调用 startForeground,然后再onDestroy里面调用stopForeground即可。
public class WhiteService extends Service {
private static final String TAG = WhiteService.class.getSimpleName();
private static final int NOTIFICATION_FLAG =0X11;
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind");
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 在Android进行通知处理,首先需要重系统哪里获得通知管理器NotificationManager,它是一个系统Service。
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// 设置点击通知跳转的Intent
Intent nfIntent = new Intent(this, MainActivity.class);
// 设置 延迟Intent
// 最后一个参数可以为PendingIntent.FLAG_CANCEL_CURRENT 或者 PendingIntent.FLAG_UPDATE_CURRENT
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, nfIntent, 0);
//构建一个Notification构造器
Notification.Builder builder = new Notification.Builder(this.getApplicationContext());
builder.setContentIntent(pendingIntent) // 设置点击跳转界面
.setLargeIcon(BitmapFactory.decodeResource(this.getResources(),
R.mipmap.fight_total_money2x)) // 设置下拉列表中的图标(大图标)
.setTicker("您有一个notification")// statusBar上的提示
.setContentTitle("这是标题") // 设置下拉列表里的标题
.setSmallIcon(R.mipmap.fight_total_order2x) // 设置状态栏内的小图标24X24
.setContentText("这是内容") // 设置详细内容
.setContentIntent(pendingIntent) // 设置点击跳转的界面
.setWhen(System.currentTimeMillis()); // 设置该通知发生的时间
.setDefaults(Notification.DEFAULT_VIBRATE) //默认震动方式
.setPriority(Notification.PRIORITY_HIGH) //优先级高
Notification notification = builder.build(); // 获取构建好的Notification
notification.defaults = Notification.DEFAULT_SOUND; //设置为默认的声音
notification.flags |= Notification.FLAG_AUTO_CANCEL; // FLAG_AUTO_CANCEL表明当通知被用户点击时,通知将被清除。
notification.flags |= FLAG_ONGOING_EVENT; //将此通知放到通知栏的"Ongoing"即"正在运行"组中
notification.flags |= FLAG_NO_CLEAR; //表明在点击了通知栏中的"清除通知"后,此通知不清除,常与FLAG_ONGOING_EVENT一起使用
manager.notify(NOTIFICATION_FLAG, notification);
// 启动前台服务
// 参数一:唯一的通知标识;参数二:通知消息。
startForeground(NOTIFICATION_FLAG, notification);// 开始前台服务
Log.d(TAG, "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
// 停止前台服务--参数:表示是否移除之前的通知
stopForeground(true);
Log.d(TAG, "onDestroy");
}
}
开启服务:
Intent intent = new Intent(MainActivity.this,WhiteService.class);
startService(intent);// (服务与开启者无联系的启动形式 )
结束服务:
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
stopService(intent);
}
});
更新通知:
重新创建notification.builder和notification
notificationManager调用 notify方法
其实就是再创建一个notification 只是标志和之前的那个一样 这样就会更新前面的通知了
二、灰色保活
也是开启前台Service,但是不会在通知栏显示
adb shell 模式下:
dumpsys activity services PackageName
若Service有 isForeground=true 而通知栏却没有显示 则是灰色保活方式
方式:
API < 18,启动前台Service时直接传入空的 new Notification();
API >= 18,在需要提优先级的service A启动一个InnerService,两个服务同时startForeground,且绑定同样的 ID。Stop 掉InnerService ,这样通知栏图标即被移除
public class GrayService extends Service {
private static final String TAG = GrayService.class.getSimpleName();
private final static int GRAY_SERVICE_ID = 0x12;
public GrayService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void onCreate() {
super.onCreate();
//API 18以下,直接发送Notification并将其置为前台
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
startForeground(GRAY_SERVICE_ID, new Notification());
} else {
//API 18以上,发送Notification并将其置为前台后,启动InnerService
Notification.Builder builder = new Notification.Builder(this);
builder.setSmallIcon(R.mipmap.fight_total_money2x);
Notification notification = builder.build(); // 获取构建好的Notification
startForeground(GRAY_SERVICE_ID, notification);
startService(new Intent(this, GrayInnerService.class));
}
Log.d(TAG, "GrayServiceOnCreate");
}
@Override
public void onDestroy() {
super.onDestroy();
// 停止前台服务--参数:表示是否移除之前的通知
stopForeground(true);
Log.d(TAG, "onDestroy");
}
}
public class GrayInnerService extends Service {
private static final String TAG = GrayInnerService.class.getSimpleName();
public static final int GRAY_INNER_SERVICE_ID=0x12;
public GrayInnerService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void onCreate() {
super.onCreate();
//发送与GrayService中ID相同的Notification,然后将其取消并取消自己的前台显示
Notification.Builder builder = new Notification.Builder(this);
builder.setSmallIcon(R.mipmap.fight_total_order2x);
Notification notification = builder.build(); // 获取构建好的Notification
startForeground(GRAY_INNER_SERVICE_ID,notification);
// 延迟0.1s 终止掉innerService 这样 通知栏图标会清除
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
stopForeground(true);
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
if (manager != null) {
manager.cancel(GRAY_INNER_SERVICE_ID);
}else {
Log.e(TAG, "notification is null!");
}
stopSelf();
}
},100);
Log.d(TAG, "GrayInnerServiceOnCreate");
}
}
三、黑色手段
利用不同的app进程使用广播来进行相互唤醒
(1)开机,网络切换、拍照、拍视频时候,利用系统产生的广播唤醒app
最新的Android N取消了 ACTION_NEW_PICTURE(拍照),ACTION_NEW_VIDEO(拍视频),CONNECTIVITY_ACTION(网络切换)等三种广播
(2)接入第三方SDK也会唤醒相应的app进程,如微信sdk会唤醒微信,支付宝sdk会唤醒支付宝
(3)app互相唤醒
四、白名单方式
系统给app加入白名单,系统不会杀死白名单中的app
五、开启一个像素透明的Activity方式
进程被杀死唤醒
待填