作者:08_carmelo

链接:

https://www.jianshu.com/p/53c4d8303e19

首先申明这篇文章非本人原创,转载的上面的朋友的,原文链接及作者就是上面的。感觉文章写的很好,所以转载了便于以后翻出来看看。

1前言

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

  1. 提高进程的优先级

  2. 在进程被kill之后能够唤醒

进程优先级

Android一般的进程优先级划分:

  1. 前台进程 (Foreground process)

  2. 可见进程 (Visible process)

  3. 服务进程 (Service process)

  4. 后台进程 (Background process)

  5. 空进程 (Empty process)

这是一种粗略的划分,进程其实有一种具体的数值,称作oom_adj,注意:数值越大优先级越低:

图片出自腾讯bugly

  • 红色部分是容易被回收的进程,属于android进程

  • 绿色部分是较难被回收的进程,属于android进程

  • 其他部分则不是android进程,也不会被系统回收,一般是ROM自带的app和服务才能拥有

如何查看某个进程的oom_adj数值呢?

oom_adj 存储在proc/PID/oom_adj文件中,其中PID是进程的id,直接 adb shell进入手机根目录查看这个文件即可。

演示一下:以我自己的项目为例,app中有两个进程,一个是主进程,另一个是运行service的进程取名为:remote。首先用android studio查看每个进程的PID:

然后分别查看app在前台,app退到后台,这2中场景主进程的oom_adj数值:

可见,当app在前台时 oom_adj = 0,对应上面的表格是前台进程。
当app退到后台时,oom_adj = 6,对应后台进程。

然后查看运行着service的进程:

ok,知道了进程优先级的概念以及如何查看优先级,我们就可以对app进程优化,然后通过查看这个数值判断我们的优化是否有效果。

2 进程被kill的场景

1.点击home键使app长时间停留在后台,内存不足被kill

处理这种情况前提是你的app至少运行了一个service,然后通过Service.startForeground() 设置为前台服务,可以将oom_adj的数值由4降低到1,大大提高存活率。

  • 要注意的是android4.3之后Service.startForeground() 会强制弹出通知栏,解决办法是再启动一个service和推送共用一个通知栏,然后stop这个service使得通知栏消失。

  • Android 7.1之后google修复这个bug,目前没有解决办法
    下面的代码放到你的service的onStartCommand方法中:

//设置service为前台服务,提高优先级if (Build.VERSION.SDK_INT < 18) {    //Android4.3以下 ,此方法能有效隐藏Notification上的图标    service.startForeground(GRAY_SERVICE_ID, new Notification());} else if(Build.VERSION.SDK_INT>18 && Build.VERSION.SDK_INT<25){    //Android4.3 - Android7.0,此方法能有效隐藏Notification上的图标    Intent innerIntent = new Intent(service, GrayInnerService.class);    service.startService(innerIntent);    service.startForeground(GRAY_SERVICE_ID, new Notification());}else{    //Android7.1 google修复了此漏洞,暂无解决方法(现状:Android7.1以上app启动后通知栏会出现一条"正在运行"的通知消息)    service.startForeground(GRAY_SERVICE_ID, new Notification());}

经过改进之后,再来看下这个后台service进程的oom_adj,发现被提升为前台进程。

2.在大多数国产手机下,进入锁屏状态一段时间,省电机制会kill后台进程

这种情况和上面不太一样,是很过国产手机rom自带的优化,当锁屏一段时间之后,即使手机内存够用为了省电,也会释放掉一部分内存。

策略:注册广播监听锁屏和解锁事件, 锁屏后启动一个1像素的透明Activity,这样直接把进程的oom_adj数值降低到0,0是android进程的最高优先级。 解锁后销毁这个透明Activity。这里我把这个Activity放到:remote进程也就是我那个后台服务进程,当然你也可以放到主进程,看你打算保活哪个进程。

我们可以写一个KeepLiveManager来负责接收广播,维护这个Activity的常见和销毁,注意锁屏广播和解锁分别是:ACTION_SCREEN_OOF和ACTION_USER_PRESENT,并且只能通过动态注册来绑定,并且是绑定到你的后台service里面,onCreate绑定,onDestroy里面解绑。

配好之后把手机锁屏,看下:remote进程的oom_adj:

3. 用户手动释放内存:包括手机自带清理工具,和第三方app(360,猎豹清理大师等)

清理内存软件会把 优先级低于 前台进程(oom_adj = 0)的所有进程放入清理列表,而当我们打开了清理软件就意味着其他app不可能处于前台。所以说理论上可以kill任何app。
以360安全卫士为例,打开内存清理:

因此这类场景唯一的处理办法就是加入 手机rom 白名单,比如你打开小米,魅族的权限管理 -> 自启动管理可以看到 QQ,微信,天猫默认被勾选,这就是厂商合作。

那我们普通app可以这么做:在app的设置界面加一个选项,提示用户自己去勾选自启动,我封装了一个工具类给出国内各厂商的自启动的Intent跳转方法:

/** * Created by carmelo on 2018/3/17. * 国内手机厂商白名单跳转工具类 */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;    }}

补充几点:

  • 额外说下 自启动是什么意思? 网上都是小米开启自启动就是加入白名单,其实根本不是这样的,经过实测就算app勾选自启动也会被内存优化加速清理掉,只不过进程会在半分钟后复活。

  • 除了还有自启动还有一个设置就是电池管理,比如小米的神隐模式,这部分和自启动不同的是它是管理app在锁屏之后被省电机制杀死的场景,当然每家厂商也有对应的Intent跳转路径。

  • 如何查找不同厂商的设置界面跳转Intent,比如上面的国内手机厂商白名单,给个方法:(前提是你有N多个不同手机,像我这样。。。)

在酷安应用市场下载一个叫 当前Activity 的app,打开后可以看到当前界面的className,例如:

就找到了魅族MX4 pro 后台权限的Activity。

3 进程唤醒

分两种情况,一是主进程(含有Activity没有service),这种进程由于内存不足被kill之后,用户再次打开app系统会恢复到上次的Activity,这个不在本文话题之内。另一种是service的后台进程被kill,可以通过service自有api来重启service:

@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {    //.....    return START_STICKY;    // service被异常停止后,系统尝试重启service,不能保证100%重启成功}

配好START_STICKY后,通过android studio 释放进程的工具测试下,可以发现:remote进程被kill之后马上重启了:

但它不是100%保证重启成功,比如下面2种情况:(本人经过测试,这里就不放效果图了)

  • Service 第一次被异常杀死后会在5秒内重启,第二次被杀死会在10秒内重启,第三次会在20秒内重启,一旦在短时间内 Service 被杀死达到5次,则系统不再拉起。

  • 进程被取得 Root 权限的管理工具或系统工具通过 forestop 停止掉,无法重启。

总结

本文通过两种 提高进程优先级的方法,针对锁屏 和非锁屏模式下进程在后台被kill的场景处理,把后台进程优先级提升到可见级别,基本可以保证绝大多数场景不会被kill。另外,针对含有service的进程被kill给出了可唤醒的办法。

最后,我将进程保活的代码上传到了Github,地址:
https://github.com/08carmelo/android-keeplive

1前言

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

  1. 提高进程的优先级

  2. 在进程被kill之后能够唤醒

进程优先级

Android一般的进程优先级划分:

  1. 前台进程 (Foreground process)

  2. 可见进程 (Visible process)

  3. 服务进程 (Service process)

  4. 后台进程 (Background process)

  5. 空进程 (Empty process)

这是一种粗略的划分,进程其实有一种具体的数值,称作oom_adj,注意:数值越大优先级越低:

图片出自腾讯bugly

  • 红色部分是容易被回收的进程,属于android进程

  • 绿色部分是较难被回收的进程,属于android进程

  • 其他部分则不是android进程,也不会被系统回收,一般是ROM自带的app和服务才能拥有


如何查看某个进程的oom_adj数值呢?

oom_adj 存储在proc/PID/oom_adj文件中,其中PID是进程的id,直接 adb shell进入手机根目录查看这个文件即可。

演示一下:以我自己的项目为例,app中有两个进程,一个是主进程,另一个是运行service的进程取名为:remote。首先用android studio查看每个进程的PID:

然后分别查看app在前台,app退到后台,这2中场景主进程的oom_adj数值:

可见,当app在前台时 oom_adj = 0,对应上面的表格是前台进程。
当app退到后台时,oom_adj = 6,对应后台进程。

然后查看运行着service的进程:

ok,知道了进程优先级的概念以及如何查看优先级,我们就可以对app进程优化,然后通过查看这个数值判断我们的优化是否有效果。

2进程被kill的场景

1.点击home键使app长时间停留在后台,内存不足被kill

处理这种情况前提是你的app至少运行了一个service,然后通过Service.startForeground() 设置为前台服务,可以将oom_adj的数值由4降低到1,大大提高存活率。

  • 要注意的是android4.3之后Service.startForeground() 会强制弹出通知栏,解决办法是再启动一个service和推送共用一个通知栏,然后stop这个service使得通知栏消失。

  • Android 7.1之后google修复这个bug,目前没有解决办法
    下面的代码放到你的service的onStartCommand方法中:

//设置service为前台服务,提高优先级if (Build.VERSION.SDK_INT < 18) {    //Android4.3以下 ,此方法能有效隐藏Notification上的图标    service.startForeground(GRAY_SERVICE_ID, new Notification());} else if(Build.VERSION.SDK_INT>18 && Build.VERSION.SDK_INT<25){    //Android4.3 - Android7.0,此方法能有效隐藏Notification上的图标    Intent innerIntent = new Intent(service, GrayInnerService.class);    service.startService(innerIntent);    service.startForeground(GRAY_SERVICE_ID, new Notification());}else{    //Android7.1 google修复了此漏洞,暂无解决方法(现状:Android7.1以上app启动后通知栏会出现一条"正在运行"的通知消息)    service.startForeground(GRAY_SERVICE_ID, new Notification());}

经过改进之后,再来看下这个后台service进程的oom_adj,发现被提升为前台进程。

2.在大多数国产手机下,进入锁屏状态一段时间,省电机制会kill后台进程


这种情况和上面不太一样,是很过国产手机rom自带的优化,当锁屏一段时间之后,即使手机内存够用为了省电,也会释放掉一部分内存。

策略:注册广播监听锁屏和解锁事件, 锁屏后启动一个1像素的透明Activity,这样直接把进程的oom_adj数值降低到0,0是android进程的最高优先级。 解锁后销毁这个透明Activity。这里我把这个Activity放到:remote进程也就是我那个后台服务进程,当然你也可以放到主进程,看你打算保活哪个进程。

我们可以写一个KeepLiveManager来负责接收广播,维护这个Activity的常见和销毁,注意锁屏广播和解锁分别是:ACTION_SCREEN_OOF和ACTION_USER_PRESENT,并且只能通过动态注册来绑定,并且是绑定到你的后台service里面,onCreate绑定,onDestroy里面解绑。

配好之后把手机锁屏,看下:remote进程的oom_adj:

3. 用户手动释放内存:包括手机自带清理工具,和第三方app(360,猎豹清理大师等)

清理内存软件会把 优先级低于 前台进程(oom_adj = 0)的所有进程放入清理列表,而当我们打开了清理软件就意味着其他app不可能处于前台。所以说理论上可以kill任何app。
以360安全卫士为例,打开内存清理:

因此这类场景唯一的处理办法就是加入 手机rom 白名单,比如你打开小米,魅族的权限管理 -> 自启动管理可以看到 QQ,微信,天猫默认被勾选,这就是厂商合作。

那我们普通app可以这么做:在app的设置界面加一个选项,提示用户自己去勾选自启动,我封装了一个工具类给出国内各厂商的自启动的Intent跳转方法:

/** * Created by carmelo on 2018/3/17. * 国内手机厂商白名单跳转工具类 */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;    }}

补充几点:

  • 额外说下 自启动是什么意思? 网上都是小米开启自启动就是加入白名单,其实根本不是这样的,经过实测就算app勾选自启动也会被内存优化加速清理掉,只不过进程会在半分钟后复活。

  • 除了还有自启动还有一个设置就是电池管理,比如小米的神隐模式,这部分和自启动不同的是它是管理app在锁屏之后被省电机制杀死的场景,当然每家厂商也有对应的Intent跳转路径。

  • 如何查找不同厂商的设置界面跳转Intent,比如上面的国内手机厂商白名单,给个方法:(前提是你有N多个不同手机,像我这样。。。)

在酷安应用市场下载一个叫 当前Activity 的app,打开后可以看到当前界面的className,例如:

就找到了魅族MX4 pro 后台权限的Activity。

3进程唤醒

分两种情况,一是主进程(含有Activity没有service),这种进程由于内存不足被kill之后,用户再次打开app系统会恢复到上次的Activity,这个不在本文话题之内。另一种是service的后台进程被kill,可以通过service自有api来重启service:

@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {    //.....    return START_STICKY;    // service被异常停止后,系统尝试重启service,不能保证100%重启成功}

配好START_STICKY后,通过android studio 释放进程的工具测试下,可以发现:remote进程被kill之后马上重启了:

但它不是100%保证重启成功,比如下面2种情况:(本人经过测试,这里就不放效果图了)

  • Service 第一次被异常杀死后会在5秒内重启,第二次被杀死会在10秒内重启,第三次会在20秒内重启,一旦在短时间内 Service 被杀死达到5次,则系统不再拉起。

  • 进程被取得 Root 权限的管理工具或系统工具通过 forestop 停止掉,无法重启。


总结


本文通过两种 提高进程优先级的方法,针对锁屏 和非锁屏模式下进程在后台被kill的场景处理,把后台进程优先级提升到可见级别,基本可以保证绝大多数场景不会被kill。另外,针对含有service的进程被kill给出了可唤醒的办法。

最后,我将进程保活的代码上传到了Github,地址:
https://github.com/08carmelo/android-keeplive


android 进程保活实践相关推荐

  1. Android进程保活(常驻内存)

    Android将进程分为6个等级,它们按优先级顺序由高到低依次是:  1.前台进程( FOREGROUND_APP):  2.可视进程(VISIBLE_APP ):  3. 次要服务进程(SECOND ...

  2. 关于 Android 进程保活,你所需要知道的一切

    早前,我在知乎上回答了这样一个问题:怎么让 Android 程序一直后台运行,像 QQ 一样不被杀死?.关于 Android 平台的进程保活这一块,想必是所有 Android 开发者瞩目的内容之一.你 ...

  3. Android进程保活

    Android进程回收机制 Low Memory Killer原理 微信团队原创分享:Android版微信后台保活实战分享(网络保活篇) 微信团队原创分享:Android版微信后台保活实战分享(进程保 ...

  4. Android 系统(146)----Android进程保活招数概览

    Android进程保活招数概览 Android中的进程保活应该分为两个方面: 提高进程的优先级,减少被系统杀死的可能性 在进程已经被杀死的情况下,通过一些手段来重新启动应用进程 本文针对这两方面来进程 ...

  5. android 进程保活6.0_Android 保活从入门到放弃:乖乖引导用户加白名单吧(附7大机型加白示例)...

    1.引言 IM在Android上的保活问题经常在即时通讯网的论坛和技术群里被讨论,自从Android 8.0后系统大大降低了后台运行应用的保活容忍度(详见<Android P正式版即将到来:后台 ...

  6. Android进程保活方案

    自己曾经也在这个问题上伤过脑经,前几日刚好有一个北京的哥们在QQ说在做IM类的项目,问我进程保活如何处理比较恰当,决定去总结一下,网上搜索一下进程常驻的方案好多好多,但是很多的方案都是不靠谱的或者不是 ...

  7. 【Android 进程保活】应用进程拉活 ( 双进程守护 + JobScheduler 保活 | 成功率最高 | 推荐使用 )

    文章目录 一. 双进程守护保活 + JobScheduler 原理 二. 双进程守护保活 + JobScheduler 源码 1.JobService 代码 2.判定服务运行工具类 3.清单文件 4. ...

  8. 【Android 进程保活】应用进程拉活 ( 账户同步拉活 | 账号添加 | 源码资源 )

    文章目录 一. 账号添加 二. 代码示例 1. 账号添加工具类 2. 权限注册 3. 在 Activity 中调用上述工具类 4. 运行效果 三. 源码资源 一. 账号添加 在上一篇博客 [Andro ...

  9. 【Android 进程保活】提升进程优先级 ( 使用前台 Service 提高应用进程优先级 | 启动相同 id 的第二个前台 Service 关闭通知 )

    文章目录 一. 前台 Service 通知问题 二. 设置 startForeground id 参数为 0 三. 启动相同 id 的第二个前台 Service 关闭通知 1. 前台服务 1 2. 关 ...

最新文章

  1. 获取轮廓、获取内接矩形
  2. php中的关联数组,PHP中的多种关联数组
  3. html 页面跳转 中文传值,两html页面之间的传值,并解决中文乱码问题
  4. mysql五-1:单表查询
  5. 802.11 n wlan linux驱动下载,802.11n无线网卡驱动
  6. java 断开socket连接_【java serversocket 长连接,客户端断开的问题】-Java技术论坛-ZOL中关村在线...
  7. iis asp mysql json_asp返回json数据,asp接口编写
  8. Linux配置java环境变量
  9. 深入浅出Python——Python高级语法之文件操作
  10. 计算机上word如何批量打印,两种批量打印多个word文档的方式
  11. 等保2.0中的工业控制系统(ICS)指的是什么
  12. Word查找替换详细用法及通配符一览表
  13. Linux网络——DNS域名解析服务
  14. [学习C++ ]C++ STL 全排列函数详解(排列组合与匹配算法)--1
  15. 搭建通过openOCD下载mini2440程序的调试平台
  16. c语言字符串dna,DNA (C语言代码)
  17. 关于网络安全法的个人理解
  18. 但行好事,去他妈的前程
  19. note9 android auto,天涯明月刀手游自动弹奏autojs脚本安装使用教程及时下流行制谱教程(包含c4和note模板)...
  20. Android OTA升级包制作和验证

热门文章

  1. 亚马逊可以卖计算机软件吗,亚马逊卖什么产品不用认证
  2. 【机器学习】独立同分布(IID)数据集
  3. 欧盟三大机构办公地点_欧盟增值税“供应地点”变更的可怕含义
  4. 关于Adobe Photoshop 无法显示原始数据,内容太大。
  5. 蒙特卡罗方法(三):铀235裂变中子谱(瓦特、maxwell)(近似修正、乘减)、方位角、质心系与实验室系散射角、中子弹散射后角度-能量分布
  6. 【Java语言基础】1.3 Java补充知识
  7. 为什么你应该为后年而不是明年规划?
  8. Js作用域与作用域链详解
  9. 怀念在东莞理工学院的日子
  10. 墨菲定律、二八法则、马太效应、手表定理、“不值得”定律、彼得原理、零和游戏、华盛顿合作规律、酒与污水定律、水桶定律、蘑菇管理原理、钱的问题、奥卡姆剃刀等13条是左右人生的金科玉律