本文首发自本人的掘金博客:https://juejin.cn/post/7003992225575075876
在开始前,还是给大家简单介绍一下,以前出现过的一些黑科技:
大概在 6 年前 Github 中出现过一个叫MarsDaemon,这个库通过双进程守护的方式实现保活,一时间风头无两。好景不长,进入 Android 8.0 时代之后,这个库就废掉了。
最近 2 年 Github 上面出来一个Leoric 感兴趣的可以去看一下源码,谁敢用在生产环境呢,也就自己玩玩的才会用吧(不能因为保活而导致手机卡巴斯基),我没有试过这个,我想说的是:黑科技能黑的了一时,能黑的了一世吗?
没有规矩,不成方圆,要提升产品的存活率,最终还是要落到产品本身上面来,尊重用户,提升用户体验才是正道。
以前我也是深受保活需求的压迫,最近发现 QQ 群里有人又提到了如何保活,那么我们就来说一说,如何来正确保活 App?
Android 8.0 之后: 加强了应用后台限制,当时测试过一组数据:
应用处于前台,启动一个前台 Service,里面使用 JobScheduler 启动定时任务(30秒触发一次),此时手机锁屏,前 10 分钟内,定时任务都是正常执行;
大概在 12 分钟左右,发现应用进程就被 kill 掉了,解锁屏幕,app 也不在前台了;
各大国产手机厂商底层都经过自己魔改,自家都有自己的一套自启动管理,小米手机更乱(当时有个神隐模式的概念,那也是杀后台高手),只能说当时 Android 手机各种性能方面都不足,各家都会有自己的一套省电模式,以此来达到省电和提高手机性能,Android 系统变得越来越完善,但是厂商定制的自启动、省电模式还在,所以我们要做保活。
1.Android 8.0 之前-常用的保活方案
1.开启一个前台 Service
2.Android 6.0+ 忽略电池优化开关(稍后会有代码)
3.无障碍服务(只针对有用这个功能的 app,如支付宝语音增强提醒用了它)
2.Android 8.0 之后-常用的保活方案
1.开启一个前台 Service(可以加上,单独启用的话无法满足保活需求)
2.Android 6.0+ 忽略电池优化开关(稍后会有代码)
3.无障碍服务(只针对有用这个功能的 app,如支付宝语音增强提醒用了它)
4.应用自启动权限(最简单的方案是针对不同系统提供教程图片-让用户自己去打开)
5.多任务列表窗口加锁(提供GIF教程图片-让用户自己去打开)
6.多任务列表窗口隐藏 App(仅针对有这方面需求的App)
7.应用后台高耗电(仅针对Vivo手机)
3.保活方案实现步骤
(1). 前台 Service
//前台服务class ForegroundCoreService : Service() { override fun onBind(intent: Intent?): IBinder? = null private var mForegroundNF:ForegroundNF by lazy { ForegroundNF(this) } override fun onCreate() { super.onCreate() mForegroundNF.startForegroundNotification() } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { if(null == intent){ //服务被系统kill掉之后重启进来的 return START_NOT_STICKY } mForegroundNF.startForegroundNotification() return super.onStartCommand(intent, flags, startId) } override fun onDestroy() { mForegroundNF.stopForegroundNotification() super.onDestroy() }}
复制代码
//初始化前台通知,停止前台通知class ForegroundNF(private val service: ForegroundCoreService) : ContextWrapper(service) { companion object { private const val START_ID = 101 private const val CHANNEL_ID = "app_foreground_service" private const val CHANNEL_NAME = "前台保活服务" } private var mNotificationManager: NotificationManager? = null private var mCompatBuilder:NotificationCompat.Builder?=null private val compatBuilder: NotificationCompat.Builder? get() { if (mCompatBuilder == null) { val notificationIntent = Intent(this, MainActivity::class.java) notificationIntent.action = Intent.ACTION_MAIN notificationIntent.addCategory(Intent.CATEGORY_LAUNCHER) notificationIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED //动作意图 val pendingIntent = PendingIntent.getActivity( this, (Math.random() * 10 + 10).toInt(), notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT ) val notificationBuilder: NotificationCompat.Builder = NotificationCompat.Builder(this,CHANNEL_ID) //标题 notificationBuilder.setContentTitle(getString(R.string.notification_content)) //通知内容 notificationBuilder.setContentText(getString(R.string.notification_sub_content)) //状态栏显示的小图标 notificationBuilder.setSmallIcon(R.mipmap.ic_coolback_launcher) //通知内容打开的意图 notificationBuilder.setContentIntent(pendingIntent) mCompatBuilder = notificationBuilder } return mCompatBuilder } init { createNotificationChannel() } //创建通知渠道 private fun createNotificationChannel() { mNotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager //针对8.0+系统 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel( CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_LOW ) channel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC channel.setShowBadge(false) mNotificationManager?.createNotificationChannel(channel) } } //开启前台通知 fun startForegroundNotification() { service.startForeground(START_ID, compatBuilder?.build()) } //停止前台服务并清除通知 fun stopForegroundNotification() { mNotificationManager?.cancelAll() service.stopForeground(true) }}
复制代码
(2).忽略电池优化(Android 6.0+)
1.我们需要在 AndroidManifest.xml 中声明一下权限
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
复制代码
2.通过 Intent 来请求忽略电池优化的权限(需要引导用户点击)
//在Activity的onCreate中注册ActivityResult,一定要在onCreate中注册//监听onActivityForResult回调mIgnoreBatteryResultContract = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { activityResult -> //查询是否开启成功 if(queryBatteryOptimizeStatus()){ //忽略电池优化开启成功 }else{ //开启失败 } }
复制代码
通过 Intent打开忽略电池优化弹框:
val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)intent.data = Uri.parse("package:$packageName")//启动忽略电池优化,会弹出一个系统的弹框,我们在上面的launchActivityResult(intent)
复制代码
查询是否成功开启忽略电池优化开关:
fun Context.queryBatteryOptimizeStatus():Boolean{ val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager? return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { powerManager?.isIgnoringBatteryOptimizations(packageName)?:false } else { true }}
复制代码
(3).无障碍服务
看官方文档:创建自己的无障碍服务
它也是一个 Service,它的优先级比较高,提供界面增强功能,初衷是帮助视觉障碍的用户或者是可能暂时无法与设备进行全面互动的用户完成操作。
可以做很多事情,使用了此 Service,在 6.0+不需要申请悬浮窗权限,直接使用WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY 挺方便的
(仅针对有需要此服务的 app,可以开启增强后台保活)
(4).自启动权限(即:白名单管理列表页面)
是系统给用户自己去打开“自启动权限”开关的入口,我们需要针对不同的手机厂商和系统版本,弹出提示引导用户是否前去打开“自启动权限”
有的手机厂商叫:白名单管理,有的叫:自启动权限,两个是一个概念;
点击查看跳转到『手机自启动设置页面』完整代码
(需要注意:如果是代码控制跳转,无法保证永远可以调整,系统升级可能就给你屏蔽了,最简单的方法是:显示一个如何找到自启动页面的引导图,下面以华为手机为例:)
华为手机-自启动管理
(5).多任务列表窗口加锁
可以针对不同手机厂商,显示引导用户,开启App窗口加锁之后,点击清理加速不会导致应用被kill
华为手机窗口加锁-教程图
(6).多任务列表窗口隐藏 App 窗口
刚刚上面多任务窗口加锁完,再提示用户去 App 里面把隐藏App窗口开关打开,这样用户就不会在多任务列表里面把 App 窗口给手抖划掉
多任务窗口中『隐藏App窗口』,可以用如下代码控制:
(这个也只是针对有这方面需求App提供的一种增强方案罢了:因为隐藏了窗口,用户就不会去想他,不会去手痒去划掉它)
//在多任务列表页面隐藏App窗口fun hideAppWindow(context: Context,isHide:Boolean){ try { val activityManager: ActivityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager //控制App的窗口是否在多任务列表显示 activityManager.appTasks[0].setExcludeFromRecents(isHide) }catch (e:Exception){ ..... } }
复制代码
(7).应用后台高耗电(Vivo手机独有)
开启的入口:“设置”>“电池”>“后台高耗电”>“找到xxxApp打开开关”
vivo允许后台高耗电
最后还是奉劝那些,仍然执着于找寻黑科技的开发者,醒醒吧,太阳晒屁股了。
如果说你的 App 用户群体不是普通用户,是专门给一些玩机大神们用的,都可以 root 手机的话,那么直接 move 到系统目录 priv/system/app 即可, 即使被用户强杀也会自动重新拉起。
评论