之前学习保活/拉活查资料写了一篇:
Android保活/拉活(一)教程检索
https://www.jianshu.com/p/b51e2dbf2311
用到了jobSchedule,但是一直在报错,然后又偶然接触到了NotificationListenerService,决定重新再折腾一次,完整代码见文章最后。
本文所有代码参考源为检索中的文章,做了整理注释及测试,转载请贴上检索链接
https://www.jianshu.com/p/b51e2dbf2311
https://www.jianshu.com/p/ce60fcedd3d6
----正文开始
假定你已经非常明白LMK的原理,那么保活的关键点在降低oom_adj值,这样app在打开后进入后台,抗击厂商清理/第三方清理,能多苟1s
建议绑定home键和back键事件
保活
环境情况
在没有任何的保活措施情况下,前台oom_adj=0,后台7
测试机型 红米4A root 6.0
只有一个主进程
方案1.开启前台服务降低oom_adj
关键代码在创建服务,开启一个带通知栏的服务(可以再开一个服务把常驻通知栏取消,实测不影响前台服务的oom_adj)
public void onCreate() {
super.onCreate();
//如果API大于18,需要弹出一个可见通知
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2) {
Notification.Builder builder = new Notification.Builder(this);
builder.setSmallIcon(R.mipmap.ic_launcher);
builder.setContentTitle("");
builder.setContentText("");
startForeground(NOTICE_ID, builder.build());
// 如果觉得常驻通知栏体验不好
// 可以通过启动CancelNoticeService,将通知移除,oom_adj值不变
Intent intent = new Intent(this, CancelNoticeService.class);
startService(intent);
} else {
startForeground(NOTICE_ID, new Notification());
}
}
测试效果,主进程在变为后台时0->7,服务oom_adj不变
实测效果
*锁屏情况和按home键进入后台一样
方案2.监听锁屏事件开启1像素界面
关键代码在监听锁屏事件创建一个1像素的activity到前台
public class ScreenBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (mStateReceiverListener == null) return;
String action = intent.getAction();
Log.d("KeepAppAlive", "SreenLockReceiver-->监听到系统广播:" + action);
if (action == null) return;
switch (action) {
case Intent.ACTION_SCREEN_ON:
mStateReceiverListener.onScreenOn();
break;
case Intent.ACTION_SCREEN_OFF:
mStateReceiverListener.onScreenOff();
break;
case Intent.ACTION_USER_PRESENT:
mStateReceiverListener.onUserPresent();
break;
}
}
}
实测效果
前台主进程oom_adj=0,后台=7,锁屏时=0
image.png
当我们查看最顶层的activity,就是我们的1像素activity
我们的1像素activity要记得setFinishOnTouchOutside(true),
避免有时候解锁杀死1像素activity不及时导致的界面卡死
至于清单为什么这样设置,请参考
image.png
方案3.循环播放无声音乐
方案1的进阶版,进liu阶mang的地方在于开启播放音乐,一些ROM是不会干掉正在播放音乐的app,测试机 华为P9,7.0,EMUI 5.0。
另一些ROM会杀掉整个进程组,测试机一加5T,7.0&8.0,氢OS。
关键代码就是播放音乐
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
@Override
public void run() {
startPlayMusic();
}
}).start();
return START_STICKY;
}
这个方案的缺点也非常明显,耗电。
拉活
方案1.JobScheduler
事务是官方推荐的做法。
我在业务中实现,然后bugly的报错信息
bugly报错
搜一下这个bug,发现是个原生bug
https://code.google.com/p/android/issues/detail?id=170814
Android Jobscheduler使用
https://www.jianshu.com/p/9fb882cae239
https://www.jianshu.com/p/045b75f7d35c
方案2.NotificationListenerService
核心在打开NLS,使得自己可以使用通知。
核心是获取通知监听权限,然后通过系统的保活来达到拉活目的。
集成NLS很简单参考
NotificationListenerService的那些事儿
关键是下面链接说的问题
https://www.zhihu.com/question/33540416
我集成之后测试,仍旧无法实现拉活效果。。。所以这一条我先写上坑。
方案3.监听系统广播并提高优先级
参考信鸽的集成方案
image.png
有三个系统广播已经在7.0关闭静态注册方式
https://www.cnblogs.com/JLZT1223/p/8108783.html
总结
因为众所周知的原因,我们没有一个统一的推送方案,如果用户不打开app,我们收不到推送的消息。
是当用户打开app后,我们尽最大努力在后台情况下活下来--保活
拉活的目的,是当app已经被杀死的情况下,尽最大努力调起--拉活
就先这样吧
代码地址:
https://github.com/lamster2018/learnDaemon
网友评论