安卓进阶_安卓中高级工程师(1/15)之25种保活机制汇总
牛客高级系列专栏:
安卓
嵌入式
本人是2020年毕业于广东工业大学研究生:许乔丹,有国内大厂CVTE和世界500强企业安卓开发经验,该专栏整理本人对安卓进阶必备知识点的理解;
网上安卓资料千千万,笔者将继续维护专栏,一杯奶茶价格不止提供答案解析,更有专栏内容免费技术答疑,15大安卓进阶必备知识点包您懂,助您提高安卓进阶技术能力,为您高薪面试保驾护航!
正文开始⬇
apk除了能够稳定高效地运行,如何提高apk的存活率,避免被系统杀死也是非常重要的知识点。下文总结了25种常见的保活机制。
目录
- 1、前言
- 1.1 Low Memory Killer
- 1.2 进程的优先级
- 2、常见的方法
- 2.1 设置成START_STICKY
- 2.2 设置为前台服务
- 2.3 AndroidManifest设置优先级
- 2.4 设置为SystemUid
- 2.5 覆写Service的onDestroy方法
- 3、另辟蹊径的方法
- 3.1 1像素保活
- 3.2 播放无声音乐
- 3.3 双进程拉活
- 3.4 多进程互保
- 3.5 通过账户同步拉活
- 3.6 推送拉活
- 3.7 闹钟循环拉起
- 3.8 引导用户关闭电池优化,允许应用后台运行
- 3.9 利用Native进程拉活
- 3.10 利用第三方服务拉起
- 4、系统相关的方法
- 4.1 设置为系统服务
- 4.2 通过系统服务重新拉起
- 4.3 系统广播拉活
- 4.4 多任务列表窗口加锁
- 4.5 多任务列表窗口隐藏App窗口
- 4.6 JobScheduler拉活
- 4.7 WorkManager
- 5、第三方库
- 5.1 MarsDaemon
- 5.2 Leoric
- 5.3 hellodaemon
1、前言
1.1 Low Memory Killer
安卓系统有一套进程回收机制:Low Memory Killer,直译为“低内存杀手”。这是因为安卓系统出于性能和体验上的考虑,会把退到后台的进程都缓存起来,在系统进程内存不足的情况下,会杀死某些退到后台的app进程。
因此需要了解apk的保活机制。如果apk在前台运行,前台应用在系统的进程优先级比较高,此时apk基本上不可能被系统杀死。只有当apk退到后台运行,系统认为该apk进程的优先级较低时,在系统资源不够时就有可能杀死apk以回收系统资源。因此,提高apk的后台存活率,核心就是提高apk在系统里的优先级。
1.2 进程的优先级
Low Memory Killer,根据 OOM_ADJ 阈值级别触发相应力度来进行内存回收。OOM_ADJ取值如下:
UNKNOWN_ADJ | 16 | 预留的最低级别,一般对于缓存的进程才有可能设置成这个级别 |
CACHED_APP_MAX_ADJ | 15 | 缓存进程,空进程,在内存不足的情况下就会优先被kill |
CACHED_APP_MIN_ADJ | 9 | 缓存进程,也就是空进程 |
SERVICE_B_ADJ | 8 | 不活跃的进程 |
PREVIOUS_APP_ADJ | 7 | 切换进程 |
HOME_APP_ADJ | 6 | 与Home交互的进程 |
SERVICE_ADJ | 5 | 有Service的进程 |
HEAVY_WEIGHT_APP_ADJ | 4 | 高权重进程 |
BACKUP_APP_ADJ | 3 | 正在备份的进程 |
PERCEPTIBLE_APP_ADJ | 2 | 可感知的进程,比如那种播放音乐 |
VISIBLE_APP_ADJ | 1 | 可见进程 |
FOREGROUND_APP_ADJ | 0 | 前台进程 |
PERSISTENT_SERVICE_ADJ | -11 | 重要进程 |
PERSISTENT_PROC_ADJ | -12 | 核心进程 |
SYSTEM_ADJ | -16 | 系统进程 |
NATIVE_ADJ | -17 | 系统起的Native进程 |
- 大于4表示容易被杀死的进程;
- 0-3是比较安全的oom_adj一般不会被系统杀死的,所以我们只要保证自己的apk的oom_adj在0-3之间就可以了。
在Low Memory Killer 回收内存时会根据进程的级别优先杀死 OOM_ADJ 比较大、用物理内存比较多的进程,对于优先级相同的进程则进一步受到进程所占内存和进程存活时间的影响。可以通过adb命令:cat /proc /进程号/oom_adj来查看自己apl的oom_adj的值。
那么,如何降低oom_adj的值,以及如何使得我们应用占的内存最少,就是提高apk存活率的根本,我们看看有哪些保活机制!
2、常见的方法
2.1 设置成START_STICKY
Service 的 onStartCommand() 返回值,当返回值为 START_STICKY 和 START_REDELIVER_INTENT时,服务会自动重启,但是 Service 在短时间内被杀死5次,则不再拉起了。
@Override
public int onStartCommand(Intent intent, int flags, int startId) {Service 的 onStartCommand 返回值,当返回值为 START_STICKY 和 START_REDELIVER_INTENT 时,服务会自动重启,但是 Service 在短时间内被杀死5次,则不再拉起
return START_STICKY;
}
2.2 设置为前台服务
这是Google官方推荐的保活方式,通过将apk服务提升为前台服务,从而拉高整个apk的优先级。使用方法是在完成Notification的创建后,在Service的onStartCommand()方法调用startForeground()方法:
//参数一:唯一的通知标识;参数二:通知消息
startForeground(int id, Notification notification);
如果要结束前台的服务,可以调用stopForeground(boolean removeNotification)来停止正在运行的前台服务,参数:
- true:删除之前发送的通知
- false:不删除 (用手滑动或者点击通知会被删除)
2.3 AndroidManifest设置优先级
数值越大,代表优先级越高,不过该方法作用不太明显。
2.4 设置为SystemUid
Android App修改为system uid,既然是系统app,优先级自然很高啦。
android:sharedUserId="android.uid.system"
2.5 覆写Service的onDestroy方法
可以在onDestroy()方法里发送一个自定义的广播,当收到广播的时候,重新启动service;
3、另辟蹊径的方法
3.1 1像素保活
当手机屏幕灭屏时,监听灭屏广播,此时偷偷创建一个只有1像素的Activity,将apk进程优先级提高到前台进程(oom_adj = 0)的优先级,同时,监听亮屏广播关闭Activity。这样就可以在用户无感知的情况下达到保活目的。
//设置1像素
Window window = getWindow();
window.setGravity(Gravity.LEFT | Gravity.TOP);
WindowManager.LayoutParams params = window.getAttributes();
params.x = 0;
params.y = 0;
params.height = 1;
params.width = 1;
window.setAttributes(params);
然而,该方法偷偷创建的Activity不够干净,且只在熄屏后生效。同时,虽然谷歌原生的系统熄屏的时候不会清理进程,但现在很多手机厂商在熄屏后会主动清除内存,所以该方案的可行性也降低了。
3.2 播放无声音乐
可以在后台播放mp3文件,同时把声音设置为0,如此提高apk在系统里的优先级。
3.3 双进程拉活
比如在进程A中的MainActivity绑定进程B中的MyService类,当绑定断开后,说明进程B挂了,此时会回调onServiceDisconnected()方法,可以在此将进程B重新拉起。
public class MainActivity extends AppCompatActivity {
private MyServiceConnection mServiceConnection;
private MyService.MyBinder mBinder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mServiceConnection = new MyServiceConnection();
Intent intent = new Intent(this, MyService.class);
bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(mServiceConnection);
}
private class MyServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
//do something
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
// 拉起进程B
}
}
}
3.4 多进程互保
这就是所谓的“全家桶拉活”,如果你同时下载了QQ,QQ空间等QQ相关的apk,只要打开其中任一apk,其他apk都会被唤醒。当这几个App都集成了同一家的推送,只要有一个App起来,就会发送一个广播,这样其它的App接收到这个广播之后,开启一个服务,就把进程给启动起来了。然而该方法不适用独立的apk。
3.5 通过账户同步拉活
手机系统设置里会有“帐户”一项功能,任何第三方APP都可以通过此功能将数据在一定时间内同步到服务器中去。系统在将APP帐户同步时,会将未启动的APP进程拉活。
3.6 推送拉活
根据终端不同,在小米手机(包括 MIUI)接入小米推送、华为手机接入华为推送;其他手机可以考虑接入腾讯信鸽或极光推送与小米推送做 A/B Test。
3.7 闹钟循环拉起
本质上也是通过设置定时任务,如果进程被杀,任务也仍然会被执行,此时就可以拉活进程。然而有概率出现存在进程死亡后,不触发的情况。而且Android 9.0的谷歌原生手机,多了一个功能,就是显示手机下一个的闹钟时间是几点,如果用到了这种保活方式,用户也注意到了这个功能,那么闹钟上的时间会暴露有应用在明目张胆的保活。
3.8 引导用户关闭电池优化,允许应用后台运行
可以引导用户关闭电池优化,允许应用在后台运行,以此提高应用在后台的存活率。
/**
* 判断我们的应用是否在白名单中(防止系统休眠时,杀死我们自己的应用)
*
* @param context 上下文对象
* @return true 在白名单中,false不在
*/
@RequiresApi(api = Build.VERSION_CODES.M)
public static boolean isIgnoringBatteryOptimizations(Context context) {
boolean isIgnoring = false;
PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
if (powerManager != null) {
isIgnoring = powerManager.isIgnoringBatteryOptimizations(context.getPackageName());
}
return isIgnoring;
}
/**
* 申请加入白名单
*
* @param context
*/
@RequiresApi(api = Build.VERSION_CODES.M)
public static void requestIgnoreBatteryOptimizations(Context context) {
try {
Intent intent = new Intent(Se
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
#提供免费售后答疑!!花一杯奶茶的钱获得安卓知识体系答疑服务,稳赚不赔# 当你已经掌握了Android基础知识,你可能会想要进一步深入学习安卓进阶知识。在这个专栏中,我们将探讨一些高级安卓开发技术,无论你是初学者还是有经验的开发者,这个专栏都将为你提供有价值的知识和经验。让我们一起开始探索安卓进阶知识的奇妙世界吧!