App保活攻防仗

五一小长假,老板特意让我去研究下Andorid黑屏如何保证app不被杀掉。

一、为什么要保活?

保活的源头是因为我们希望自己的进程或服务能够一直在后台运行,但是总有各种各样的原因导致我们希望破灭。

失活的原因
  • 1、触发Android内存回收机制

Android系统内存不足时,系统会杀掉一部分进程以释放空间,保证系统使用流畅。谁生谁死的这个生死大权就是由LMK所决定的,Android系统中的Low Memory Killer(基于Linux内核的OOM Killer,即Out-Of-Memory killer)。简单地说,LMK根据“当内存小于X时,结束oom_adj值大于Y的进程”这样的判断来销毁进程,回收内存。

  • 2、手机厂商定制管理系统,如电源管理、内存管理等
    深度定制,如小米、华为、魅族等。
  • 3、第三方清理软件
  • 4、用户手动结束

二、保活手段

提高进程优先级的方案
1、锁屏推一个Activity到前台

思路:监控手机解锁屏时间,在屏幕锁屏时启动1个像素的 Activity,在用户解锁时将 Activity 销毁掉。注意该 Activity 需设计成用户无感知。
适用场景:本方案主要解决第三方应用及系统管理工具在检测到锁屏事件后一段时间(一般为5分钟以内)内会杀死后台进程,已达到省电的目的问题。
适用版本:适用于所有的 Android 版本。
缺点:解锁后,会看到当前App的Activity,用户感知不好。

2、利用Notification,将后台Service提升为前台Service

思想:Android 中 Service 的优先级为4,通过 setForeground 接口可以将后台 Service 设置为前台 Service,使进程的优先级由4提升为2,从而使进程的优先级仅仅低于用户当前正在交互的进程,使进程被杀死的概率大大降低。
缺点:从 Android2.3 开始调用 setForeground 将后台 Service 设置为前台 Service 时,必须在系统的通知栏发送一条通知,也就是前台 Service 与一条可见的通知时绑定在一起的。
(7.0之前可通过实现一个内部 Service,在 LiveService 和其内部 Service 中同时发送具有相同 ID 的 Notification,然后将内部 Service 结束掉。随着内部 Service 的结束,Notification 将会消失,但系统优先级依然保持为2。Android7.0版本已修复该bug)

3、联系手机厂商,加入白名单
4、利用系统广播

思想:在发生特定系统事件时,系统会发出响应的广播,通过注册对应的广播监听器,即可在发生响应事件时拉活。
常用的广播事件包括:
开机广播—-RECEIVE_BOOT_COMPLETED
网络变化—-ACCESS_NETWORK_STATE、CHANG_NETWORK_STATE、ACCESS_WIFI_STATE、CHANGE_WIFI_STATE、ACCESS_FINE_LOCATION、ACCESS_LOCATION_EXTRA_COMMANDS
文件挂载—-MOUNT_UNMOUNT_FILESYSTEMS
屏幕亮灭—–SCREEN_ON、SCREEN_OFF
解锁屏幕—–RECEIVE_USER_PRESENT
应用安装卸载—PACKAGE_ADDED、PACKAGE_REMOVED

静态广播
exported默认为true表示这个广播可以接收来自其他app发送的广播,只要条件满足,exported设置成false表示只能是这个app内发送的广播才能接收,即使是receiver的进程和发送广播的进程不是同一个,但是只要都是属于一个app的就可以正常接收,有时候会遇到发送者和接收者不在一个进程,广播没办法正常接收,这是因为receiver所在的进程是死的,如果通过某种方式把receiver所在的进程唤醒,那么即使exported为false也能正常接收
enabled为true表示广播可用,为false表示禁用广播,禁用后广播将无法接收

适用范围:适用于全部 Android 平台。但存在如下几个缺点:
1、广播接收器被管理软件、系统软件通过“自启管理”等功能禁用的场景无法接收到广播,从而无法自启。
2、如果app被杀死后就无法收到了,这种手段无能为力。
3、理论上好像可以通过另一个app来启动keep-alive的app(待实验…)

利用WakeLock唤醒

思路:通过监听home键,调用WakeLock.acquire();唤醒。
测试:华为荣耀7 Android6.0、Vivo Android6.0.1无效。

任务清理杀不死方案

1、利用系统通知服务拉活
思想:自定义NotificationListenerService与app在同一进程中,保证app杀不死。
适用范围:Android 4.3及以上。华为荣耀7 6.0版本测试可行。
缺点:需要引导用户开启通知权限,用户感知差。

//1、监听系统通知,需要用户手动开启权限,那么该进程可以不死 
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public class MyListenerService extends NotificationListenerService {
    @Override
    public void onNotificationPosted(StatusBarNotification sbn) {
    }

    @Override
    public void onNotificationRemoved(StatusBarNotification sbn) {
    }
}
//2、在清单文件定义
<service 
    android:name=".way4.MyListenerService"
    android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
    <intent-filter>
        <action android:name="android.service.notification.NotificationListenerService"/>
    </intent-filter>
</service>

附录

一点概念

1、oom_adj:
Linux内核分配给每个系统进程的值,代表进程优先级。
每一个oom_adj值都会有相应的最小内存阀值,这部分值在lowmemorykiller.c文件有定义。当系统内存等于或小于该最小内存阀值,会回收大于对应oom_adj值的进程。
2、XXX:
XXXXXXXXXXXXXX
3、进程的生命周期(oom_adj改变)参见官网进程和线程一文
其中的空进程好像就是手机中的后台缓存进程。正在运行的进程:需要占用一定的cpu资源和RAM(内存)空间,多少的话看是什么应用,要消耗一定的电量,影响手机速度等性能。后台缓存的进程:不需要占用cpu资源,会在RAM中写入一部分数据,当下次打开这个应用时会快一些,当然也会占用一点内存,如果手机内存够大对速度性能不会有影响,如果内存不够那么会触发android的内存回收机制,优先杀掉这些进程(和java的垃圾回收机制相似),会耗一点点电,几乎没什么影响,只是在内存的某些位上写了地址数据,每隔一段时间过一遍电脉冲,刷新来保持数据。

ProcessList.java(定义进程优先级)位置

static final int UNKNOWN_ADJ = 16;//一般只将要缓存进程,无法获取确定值
static final int CACHED_APP_MAX_ADJ = 15;//不可见进程的adj最大值1
static final int CACHED_APP_MIN_ADJ = 9;//不可见进程的adj最小值2
static final int SERVICE_B_ADJ = 8;//B List中的Service(较老的,使用可能性更小)
static final int PREVIOUS_APP_ADJ = 7;//上一个App的进程(往往通过按返回键)
static final int HOME_APP_ADJ = 6;//Home进程
static final int SERVICE_ADJ = 5;//服务进程
static final int HEAVY_WEIGHT_APP_ADJ = 4;//后台重量级进程,system/rootdir/init.rc文件中设置

static final int BACKUP_APP_ADJ = 3;//备份进程
static final int PERCEPTIBLE_APP_ADJ = 2;//可预感进程,比较后台音乐播放
static final int VISIBLE_APP_ADJ = 1;//可见进程
static final int FOREGROUND_APP_ADJ = 0;//前台进程

static final int PERSISTENT_SERVICE_ADJ = -11;//关联着系统或persisten进程
static final int PERSISTENT_PROC_ADJ = -12;//系统persisten进程,比如telephony
static final int SYSTEM_ADJ = -16;//系统进程
static final int NATIVE_ADJ = -17;//native进程(不被系统管理)

lowmemorykiller.c文件(定义内存回收策略)

static uint32_t lowmem_debug_level = 1;
static int lowmem_adj[6] = {
    0,
    1,
    6,
    12,
};
static int lowmem_adj_size = 4;
static int lowmem_minfree[6] = {
    3 * 512,    /* 6MB */
    2 * 1024,   /* 8MB */
    4 * 1024,   /* 16MB */
    16 * 1024,  /* 64MB */
};
static int lowmem_minfree_size = 4;

小技巧
手机Root推荐用King Root,挺人性化!

1、查看某个App进程的优先级
步骤(手机与PC连接)
adb shell
ps | grep 进程名
cat /proc/pid/oom_adj //其中pid是上述grep得到的进程号

2、Linux AM命令
am命令:在Android系统中通过adb shell 启动某个Activity、Service、拨打电话、启动浏览器等操作Android的命令.其源码在Am.java中,在shell环境下执行am命令实际是启动一个线程执行Am.java中的主函数(main方法),am命令后跟的参数都会当做运行时参数传递到主函数中,主要实现在Am.java的run方法中。

拨打电话
命令:am start -a android.intent.action.CALL -d tel:电话号码
示例:am start -a android.intent.action.CALL -d tel:10086

打开一个网页
命令:am start -a android.intent.action.VIEW -d 网址
示例:am start -a android.intent.action.VIEW -d http://www.skyseraph.com

启动一个服务
命令:am startservice <服务名称>
示例:am startservice -n com.android.music/ com.android.music.MediaPlaybackService

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值