Android应用保活实践详解
最近在做的项目中需要app在后台常驻,用于实时上传一些健康信息数据,便于后台实时查看用户的健康状况。自从android7.0以上后台常驻实现越来越难,尤其是8.0及以上。关于保活的文章比比皆是,但是效果并不理想,关于保活的方法也就常说的哪几种,重点在于怎么组合运用。最终实现效果为:用户不主动强制杀死的话,能够一直存活(小米,华为,vivo,oppo,三星)。其中三星s8,华为nova2s用户强制杀死也能存活。
项目结构
常见的保活方案
关于android应用保活的文章很多,这里不再阐述,可自行百度。重点在于运用这样方案来实现保活功能。
代码实现
1.监听锁屏广播,开启1个像素的activity。
在锁屏的时候启动一个1个像素的activity,当用户解锁以后将这个activity结束掉。
定义一个1像素的activity,在该activity中动态注册自定义的广播。
class onepixelactivity : appcompatactivity() { private lateinit var br: broadcastreceiver override fun oncreate(savedinstancestate: bundle?) { super.oncreate(savedinstancestate) //设定一像素的activity val window = window window.setgravity(gravity.left or gravity.top) val params = window.attributes params.x = 0 params.y = 0 params.height = 1 params.width = 1 window.attributes = params //在一像素activity里注册广播接受者 接受到广播结束掉一像素 br = object : broadcastreceiver() { override fun onreceive(context: context, intent: intent) { finish() } } registerreceiver(br, intentfilter("finish activity")) checkscreenon() } override fun onresume() { super.onresume() checkscreenon() } override fun ondestroy() { try { //销毁的时候解锁广播 unregisterreceiver(br) } catch (e: illegalargumentexception) { } super.ondestroy() } /** * 检查屏幕是否点亮 */ private fun checkscreenon() { val pm = this@onepixelactivity.getsystemservice(context.power_service) as powermanager val isscreenon = if (build.version.sdk_int >= build.version_codes.kitkat_watch) { pm.isinteractive } else { pm.isscreenon } if (isscreenon) { finish() } } }
2.双进程守护
定义一个本地服务,在该服务中播放无声音乐,并绑定远程服务。
class localservice : service() { private var mediaplayer: mediaplayer? = null private var mbilder: mybilder? = null override fun oncreate() { super.oncreate() if (mbilder == null) { mbilder = mybilder() } } override fun onbind(intent: intent): ibinder? { return mbilder } override fun onstartcommand(intent: intent, flags: int, startid: int): int { //播放无声音乐 if (mediaplayer == null) { mediaplayer = mediaplayer.create(this, r.raw.novioce) //声音设置为0 mediaplayer?.setvolume(0f, 0f) mediaplayer?.islooping = true//循环播放 play() } //启用前台服务,提升优先级 if (keeplive.foregroundnotification != null) { val intent2 = intent(applicationcontext, notificationclickreceiver::class.java) intent2.action = notificationclickreceiver.click_notification val notification = notificationutils.createnotification(this, keeplive.foregroundnotification!!.gettitle(), keeplive.foregroundnotification!!.getdescription(), keeplive.foregroundnotification!!.geticonres(), intent2) startforeground(13691, notification) } //绑定守护进程 try { val intent3 = intent(this, remoteservice::class.java) this.bindservice(intent3, connection, context.bind_above_client) } catch (e: exception) { } //隐藏服务通知 try { if (build.version.sdk_int < 25) { startservice(intent(this, hideforegroundservice::class.java)) } } catch (e: exception) { } if (keeplive.keepliveservice != null) { keeplive.keepliveservice!!.onworking() } return service.start_sticky } private fun play() { if (mediaplayer != null && !mediaplayer!!.isplaying) { mediaplayer?.start() } } private inner class mybilder : guardaidl.stub() { @throws(remoteexception::class) override fun wakeup(title: string, discription: string, iconres: int) { } } private val connection = object : serviceconnection { override fun onservicedisconnected(name: componentname) { val remoteservice = intent(this@localservice, remoteservice::class.java) this@localservice.startservice(remoteservice) val intent = intent(this@localservice, remoteservice::class.java) this@localservice.bindservice(intent, this, context.bind_above_client) } override fun onserviceconnected(name: componentname, service: ibinder) { try { if (mbilder != null && keeplive.foregroundnotification != null) { val guardaidl = guardaidl.stub.asinterface(service) guardaidl.wakeup(keeplive.foregroundnotification?.gettitle(), keeplive.foregroundnotification?.getdescription(), keeplive.foregroundnotification!!.geticonres()) } } catch (e: remoteexception) { e.printstacktrace() } } } override fun ondestroy() { super.ondestroy() unbindservice(connection) if (keeplive.keepliveservice != null) { keeplive.keepliveservice?.onstop() } } }
定义一个远程服务,绑定本地服务。
class remoteservice : service() { private var mbilder: mybilder? = null override fun oncreate() { super.oncreate() if (mbilder == null) { mbilder = mybilder() } } override fun onbind(intent: intent): ibinder? { return mbilder } override fun onstartcommand(intent: intent, flags: int, startid: int): int { try { this.bindservice(intent(this@remoteservice, localservice::class.java), connection, context.bind_above_client) } catch (e: exception) { } return service.start_sticky } override fun ondestroy() { super.ondestroy() unbindservice(connection) } private inner class mybilder : guardaidl.stub() { @throws(remoteexception::class) override fun wakeup(title: string, discription: string, iconres: int) { if (build.version.sdk_int < 25) { val intent = intent(applicationcontext, notificationclickreceiver::class.java) intent.action = notificationclickreceiver.click_notification val notification = notificationutils.createnotification(this@remoteservice, title, discription, iconres, intent) this@remoteservice.startforeground(13691, notification) } } } private val connection = object : serviceconnection { override fun onservicedisconnected(name: componentname) { val remoteservice = intent(this@remoteservice, localservice::class.java) this@remoteservice.startservice(remoteservice) this@remoteservice.bindservice(intent(this@remoteservice, localservice::class.java), this, context.bind_above_client) } override fun onserviceconnected(name: componentname, service: ibinder) {} } } /** * 通知栏点击广播接受者 */ class notificationclickreceiver : broadcastreceiver() { companion object { const val click_notification = "click_notification" } override fun onreceive(context: context, intent: intent) { if (intent.action == notificationclickreceiver.click_notification) { if (keeplive.foregroundnotification != null) { if (keeplive.foregroundnotification!!.getforegroundnotificationclicklistener() != null) { keeplive.foregroundnotification!!.getforegroundnotificationclicklistener()?.foregroundnotificationclick(context, intent) } } } } }
3.jobscheduler
jobscheduler和jobservice是安卓在api 21中增加的接口,用于在某些指定条件下执行后台任务。
定义一个jobservice,开启本地服务和远程服务
@suppresswarnings(value = ["unchecked", "deprecation"]) @requiresapi(build.version_codes.lollipop) class jobhandlerservice : jobservice() { private var mjobscheduler: jobscheduler? = null override fun onstartcommand(intent: intent?, flags: int, startid: int): int { var startid = startid startservice(this) if (build.version.sdk_int >= build.version_codes.lollipop) { mjobscheduler = getsystemservice(context.job_scheduler_service) as jobscheduler val builder = jobinfo.builder(startid++, componentname(packagename, jobhandlerservice::class.java.name)) if (build.version.sdk_int >= 24) { builder.setminimumlatency(jobinfo.default_initial_backoff_millis) //执行的最小延迟时间 builder.setoverridedeadline(jobinfo.default_initial_backoff_millis) //执行的最长延时时间 builder.setminimumlatency(jobinfo.default_initial_backoff_millis) builder.setbackoffcriteria(jobinfo.default_initial_backoff_millis, jobinfo.backoff_policy_linear)//线性重试方案 } else { builder.setperiodic(jobinfo.default_initial_backoff_millis) } builder.setrequirednetworktype(jobinfo.network_type_any) builder.setrequirescharging(true) // 当插入充电器,执行该任务 mjobscheduler?.schedule(builder.build()) } return service.start_sticky } private fun startservice(context: context) { if (build.version.sdk_int >= build.version_codes.o) { if (keeplive.foregroundnotification != null) { val intent = intent(applicationcontext, notificationclickreceiver::class.java) intent.action = notificationclickreceiver.click_notification val notification = notificationutils.createnotification(this, keeplive.foregroundnotification!!.gettitle(), keeplive.foregroundnotification!!.getdescription(), keeplive.foregroundnotification!!.geticonres(), intent) startforeground(13691, notification) } } //启动本地服务 val localintent = intent(context, localservice::class.java) //启动守护进程 val guardintent = intent(context, remoteservice::class.java) startservice(localintent) startservice(guardintent) } override fun onstartjob(jobparameters: jobparameters): boolean { if (!isservicerunning(applicationcontext, "com.xiyang51.keeplive.service.localservice") || !isservicerunning(applicationcontext, "$packagename:remote")) { startservice(this) } return false } override fun onstopjob(jobparameters: jobparameters): boolean { if (!isservicerunning(applicationcontext, "com.xiyang51.keeplive.service.localservice") || !isservicerunning(applicationcontext, "$packagename:remote")) { startservice(this) } return false } private fun isservicerunning(ctx: context, classname: string): boolean { var isrunning = false val activitymanager = ctx .getsystemservice(context.activity_service) as activitymanager val serviceslist = activitymanager .getrunningservices(integer.max_value) val l = serviceslist.iterator() while (l.hasnext()) { val si = l.next() if (classname == si.service.classname) { isrunning = true } } return isrunning } }
4.播放无声音乐
这里使用的是有声的mp3文件,只是在代码中把声音设置成了0;如果使用真正的无声的音乐文件,在oppo手机上按下返回键会被立刻杀死,并且在三星手机,华为nova2s强制杀死也会被杀死,所有使用了有声的文件。
5.提高service优先级
在 onstartcommand()
方法中开启一个通知,提高进程的优先级。注意:从android 8.0(api级别26)开始,所有通知必须要分配一个渠道,对于每个渠道,可以单独设置视觉和听觉行为。然后用户可以在设置中修改这些设置,根据应用程序来决定哪些通知可以显示或者隐藏。
定义一个通知工具类,兼容8.0
class notificationutils(context: context) : contextwrapper(context) { private var manager: notificationmanager? = null private var id: string = context.packagename + "51" private var name: string = context.packagename private var context: context = context private var channel: notificationchannel? = null companion object { @suppresslint("staticfieldleak") private var notificationutils: notificationutils? = null fun createnotification(context: context, title: string, content: string, icon: int, intent: intent): notification? { if (notificationutils == null) { notificationutils = notificationutils(context) } var notification: notification? = null notification = if (build.version.sdk_int >= 26) { notificationutils?.createnotificationchannel() notificationutils?.getchannelnotification(title, content, icon, intent)?.build() } else { notificationutils?.getnotification_25(title, content, icon, intent)?.build() } return notification } } @requiresapi(api = build.version_codes.o) fun createnotificationchannel() { if (channel == null) { channel = notificationchannel(id, name, notificationmanager.importance_min) channel?.enablelights(false) channel?.enablevibration(false) channel?.vibrationpattern = longarrayof(0) channel?.setsound(null, null) getmanager().createnotificationchannel(channel) } } private fun getmanager(): notificationmanager { if (manager == null) { manager = getsystemservice(context.notification_service) as notificationmanager } return manager!! } @requiresapi(api = build.version_codes.o) fun getchannelnotification(title: string, content: string, icon: int, intent: intent): notification.builder { //pendingintent.flag_update_current 这个类型才能传值 val pendingintent = pendingintent.getbroadcast(context, 0, intent, pendingintent.flag_update_current) return notification.builder(context, id) .setcontenttitle(title) .setcontenttext(content) .setsmallicon(icon) .setautocancel(true) .setcontentintent(pendingintent) } fun getnotification_25(title: string, content: string, icon: int, intent: intent): notificationcompat.builder { val pendingintent = pendingintent.getbroadcast(context, 0, intent, pendingintent.flag_update_current) return notificationcompat.builder(context, id) .setcontenttitle(title) .setcontenttext(content) .setsmallicon(icon) .setautocancel(true) .setvibrate(longarrayof(0)) .setsound(null) .setlights(0, 0, 0) .setcontentintent(pendingintent) } }
使用
将保活的功能封装成了一个单独的库,依赖该库即可。
app中使用:
keeplive.startwork(this, keeplive.runmode.rogue, foregroundnotification("title", "message", r.mipmap.ic_launcher, object : foregroundnotificationclicklistener { override fun foregroundnotificationclick(context: context, intent: intent) { //点击通知回调 } }), object : keepliveservice { override fun onstop() { //可能调用多次,跟onworking匹配调用 } override fun onworking() { //一直存活,可能调用多次 } })
清单文件配置:
<!--权限配置--> <uses-permission android:name="android.permission.foreground_service" /> <uses-permission android:name="android.permission.get_tasks" /> <uses-permission android:name="android.permission.reorder_tasks" /> <!--保活相关配置--> <receiver android:name="com.xiyang51.keeplive.receiver.notificationclickreceiver" /> <activity android:name="com.xiyang51.keeplive.activity.onepixelactivity" /> <service android:name="com.xiyang51.keeplive.service.localservice" /> <service android:name="com.xiyang51.keeplive.service.hideforegroundservice" /> <service android:name="com.xiyang51.keeplive.service.jobhandlerservice" android:permission="android.permission.bind_job_service" /> <service android:name="com.xiyang51.keeplive.service.remoteservice" android:process=":remote" />
代码地址 github
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: C#中实现网段扫描的代码
下一篇: c#下注册表操作的一个小细节