进程是什么?
进程的分类?
Android在内存较低的情况下,会关闭一些优先级较低的进程以增大内存运行更重要的进程,而在这个进程中的所有线程,也会被同时销毁。
Android中,进程的生命周期都是由系统控制的。即使用户在界面上关掉一个应用,切换到了别的应用,那个应用的进程依然是存在于内存之中的。这样设计的目的是为了下次启动应用能更加快速。当然,随着系统运行时间的增长,内存中的进程可能会越来越多,而可用的内存则将会越来越少。Android Kernel会定时执行一次检查,杀死一些进程,释放掉内存。
Android一般的进程优先级划分:
1.前台进程 (Foreground process)
2.可见进程 (Visible process)
3.服务进程 (Service process)
4.后台进程 (Background process)
5.空进程 (Empty process)
进程其实有一种具体的数值,称作oom_adj,注意:数值越大优先级越低
进程的回收机制?
系统出于体验和性能上的考虑,app在退到后台时系统并不会真正的kill掉这个进程,而是将其缓存起来。打开的应用越多,后台缓存的进程也越多。在系统内存不足的情况下,系统开始依据自身的一套进程回收机制来判断要kill掉哪些进程,以腾出内存来供给需要的app。这套杀进程回收内存的机制就叫 Low Memory Killer ,它是基于Linux内核的 OOM Killer(Out-Of-Memory killer)机制诞生。
Android 的 low memory killer 是基于 linux 的OOM(out of memory)规则改进而来的。OOM 通过一些比较复杂的评分机制,对进程进行打分,然后将分数高的进程判定为 bad进程,杀死进程并释放内存。OOM 只有当系统内存不足的时候才会启动检查,而 low memory killer 则不仅是在应用程序分配内存发现内存不足时启动检查,它也会定时地进行检查。
Low memory killer 主要是通过进程的 oom_adj 来判定进程的重要程度的。oom_adj 的大小和进程的类型以及进程被调度的次序有关
在 linux 中,存在一个名为 kswapd 的内核线程,当linux回收存放分页的时候,kswapd 线程将会遍历一张 shrinker 链表,并执行回调,或者某个app分配内存,发现可用内存不足时,则内核会阻塞请求分配内存的进程分配内存的过程,并在该进程中去执行lowmemorykiller来释放内存
进程被kill的场景?
1.点击home键使app长时间停留在后台,内存不足被kill
2.在大多数国产手机下,进入锁屏状态一段时间,省电机制会kill后台进程
进程的优先级
什么是oom_adj?它是linux内核分配给每个系统进程的一个值,代表进程的优先级,进程回收机制就是根据这个优先级来决定是否进行回收。对于oom_adj的作用,你只需要记住以下几点即可:
进程的oom_adj越大,表示此进程优先级越低,越容易被杀回收;越小,表示进程优先级越高,越不容易被杀回收
普通app进程的oom_adj>=0,系统进程的oom_adj才可能<0
一般前台的进程优先级oom_adj = 0 ,只有系统进程的oom_adj才会小于0
ps | grep PackageName (com.ypcang.android.shop) //查看此app全部进程
cat /proc/进程号(19848)/oom_adj //查看某一进程的优先级
C:\Users\Administrator>adb shell
shell@hwALE-H:/ $ ps | grep com.example.administrator.shadowapplication
u0_a348 29047 2466 1591236 44684 SyS_epoll_ 0000000000 S com.example.administrator.shadowapplication
u0_a348 29252 2466 1550308 35520 SyS_epoll_ 0000000000 S com.example.administrator.shadowapplication:gray
u0_a348 29590 2466 1549224 55980 SyS_epoll_ 0000000000 S com.example.administrator.shadowapplication:white
shell@hwALE-H:/ $ cat /proc/29590/oom_adj
1
shell@hwALE-H:/ $ cat /proc/29252/oom_adj
1
shell@hwALE-H:/ $ cat /proc/29047/oom_adj
0
shell@hwALE-H:/ $ cat /proc/29047/oom_adj
6
shell@hwALE-H:/ $ cat /proc/29252/oom_adj
1
shell@hwALE-H:/ $ cat /proc/29590/oom_adj
1
shell@hwALE-H:/ $ ps | grep com.example.administrator.shadowapplication
u0_a348 29047 2466 1577052 50420 SyS_epoll_ 0000000000 S com.example.administrator.shadowapplication
u0_a348 29252 2466 1550308 35520 SyS_epoll_ 0000000000 S com.example.administrator.shadowapplication:gray
u0_a348 29590 2466 1549224 55984 SyS_epoll_ 0000000000 S com.example.administrator.shadowapplication:white
当app在前台时 oom_adj = 0,对应上面的表格是前台进程。
当app退到后台时,oom_adj = 6,对应后台进程。
app退到后台时,其所有的进程优先级都会降低。但是UI进程是降低最为明显的,因为它占用的内存资源最多,系统内存不足的时候肯定优先杀这些占用内存高的进程来腾出资源。所以,为了尽量避免后台UI进程被杀,需要尽可能的释放一些不用的资源,尤其是图片、音视频之类的。
进程的保活
1、AIDL方式单进程、双进程方式保活Service
2、降低oom_adj的值:常驻通知栏(可通过启动另外一个服务关闭Notification,不对oom_adj值有影响)、使用”1像素“的Activity覆盖在getWindow()的view上、循环播放无声音频(黑科技,7.0下杀不掉)
3、监听锁屏广播:使Activity始终保持前台
4、使用自定义锁屏界面:覆盖了系统锁屏界面。
5、通过android:process属性来为Service创建一个进程
6、跳转到系统白名单界面让用户自己添加app进入白名单
加入 手机rom 白名单,比如你打开小米,魅族的权限管理 -> 自启动管理可以看到 QQ,微信,天猫默认被勾选,这就是厂商合作。那我们普通app可以这么做:在app的设置界面加一个选项,提示用户自己去勾选自启动
public class SettingUtils {
public static void enterWhiteListSetting(Context context){
try {
context.startActivity(getSettingIntent());
}catch (Exception e){
context.startActivity(new Intent(Settings.ACTION_SETTINGS));
}
}
private static Intent getSettingIntent(){
ComponentName componentName = null;
String brand = android.os.Build.BRAND;
switch (brand.toLowerCase()){
case "samsung":
componentName = new ComponentName("com.samsung.android.sm",
"com.samsung.android.sm.app.dashboard.SmartManagerDashBoardActivity");
break;
case "huawei":
componentName = new ComponentName("com.huawei.systemmanager",
"com.huawei.systemmanager.startupmgr.ui.StartupNormalAppListActivity");
break;
case "xiaomi":
componentName = new ComponentName("com.miui.securitycenter",
"com.miui.permcenter.autostart.AutoStartManagementActivity");
break;
case "vivo":
componentName = new ComponentName("com.iqoo.secure",
"com.iqoo.secure.ui.phoneoptimize.AddWhiteListActivity");
break;
case "oppo":
componentName = new ComponentName("com.coloros.oppoguardelf",
"com.coloros.powermanager.fuelgaue.PowerUsageModelActivity");
break;
case "360":
componentName = new ComponentName("com.yulong.android.coolsafe",
"com.yulong.android.coolsafe.ui.activity.autorun.AutoRunListActivity");
break;
case "meizu":
componentName = new ComponentName("com.meizu.safe",
"com.meizu.safe.permission.SmartBGActivity");
break;
case "oneplus":
componentName = new ComponentName("com.oneplus.security",
"com.oneplus.security.chainlaunch.view.ChainLaunchAppListActivity");
break;
default:
break;
}
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if(componentName!=null){
intent.setComponent(componentName);
}else{
intent.setAction(Settings.ACTION_SETTINGS);
}
return intent;
}
}
所谓保活就是指程序进入后台,不被系统杀死
当前业界的Android进程保活手段主要分为 黑、白、灰 三种,其大致的实现思路如下:
黑色保活:不同的app进程,用广播相互唤醒(包括利用系统提供的广播进行唤醒)
在最新的Android N取消了 ACTION_NEW_PICTURE(拍照),ACTION_NEW_VIDEO(拍视频),CONNECTIVITY_ACTION(网络切换)等三种广播,无疑给了很多app沉重的打击。我猜他们的心情是下面这样的
白色保活:启动前台Service
白色保活手段非常简单,就是调用系统api启动一个前台的Service进程,这样会在系统的通知栏生成一个Notification,用来让用户知道有这样一个app在运行着,哪怕当前的app退到了后台。
灰色保活:利用系统的漏洞启动前台Service
它是利用系统的漏洞来启动一个前台的Service进程,与普通的启动方式区别在于,它不会在系统通知栏处出现一个Notification,看起来就如同运行着一个后台Service进程一样。这样做带来的好处就是,用户无法察觉到你运行着一个前台进程(因为看不到Notification),但你的进程优先级又是高于普通后台进程的。
思路一:API < 18,启动前台Service时直接传入new Notification();
思路二:API >= 18,同时启动两个id相同的前台Service,然后再将后启动的Service做stop处理;
public class GrayService extends Service {
private final static int GRAY_SERVICE_ID = 1001;
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (Build.VERSION.SDK_INT < 18) {
startForeground(GRAY_SERVICE_ID, new Notification());//API < 18 ,此方法能有效隐藏Notification上的图标
} else {
Intent innerIntent = new Intent(this, GrayInnerService.class);
startService(innerIntent);
startForeground(GRAY_SERVICE_ID, new Notification());
}
return super.onStartCommand(intent, flags, startId);
}
/**
* 给 API >= 18 的平台上用的灰色保活手段
*/
public static class GrayInnerService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
startForeground(GRAY_SERVICE_ID, new Notification());
stopForeground(true);
stopSelf();
return super.onStartCommand(intent, flags, startId);
}
}
}
dumpsys activity services PackageName:查看应用的信息,如果 isForeground=true,且没有通知,说明此应用启动了灰色保活
灰色保活并不代表着你的Service就永生不死了,只能说是提高了进程的优先级。如果你的app进程占用了大量的内存,按照回收进程的策略,同样会干掉你的app。
有些手机厂商把这些知名的app放入了自己的白名单中,保证了进程不死来提高用户体验(如微信、QQ、陌陌都在小米的白名单中)。如果从白名单中移除,他们终究还是和普通app一样躲避不了被杀的命运,为了尽量避免被杀,还是老老实实去做好优化工作吧。
在进程被kill之后能够唤醒
总结
进程保活对于Android6.0以及以下的大部分机型还是有效果的,但是Android7.0和Android8.0基本上所有机型全部阵亡,大部分后台进程在锁屏后无法存活超过20分钟。
这个可以从Android 6.0,7.0和8.0的新特性看出一些端倪,google对于内存/电量使用越来越严格。
鉴于目前国内大部分手机基本都到7.0了,进程保活暂时可以说是宣告失败了。除了加入厂商白名单之外我不认为有真正可行的方案,毕竟google近几个Android版本不停在系统层面限制,我们在app层没有很大的发挥空间,so,之前的方案只能作为一个优化策略吧
参考链接
(android 进程保活知识了解)[https://www.cnblogs.com/dongweiq/p/5404331.html]
(Android平台下APP唤醒机制)[https://blog.csdn.net/daijin888888/article/details/53129884]
(android 进程保活大全)[https://www.cnblogs.com/bugly/p/5765334.html]
(详细的APP保活步骤)[https://mp.weixin.qq.com/s/KOFWqaSlrgNck2_YDLur_Q]
(2018进程保活方案,全面,代码)[https://www.jianshu.com/p/b5371df6d7cbt]
(android 进程保活实践)[https://www.jianshu.com/p/53c4d8303e19]
(很详细的进程保活策略代码)[https://blog.csdn.net/nazicsdn/article/details/79752617]