欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android应用保活实践详解

程序员文章站 2022-06-21 10:02:16
最近在做的项目中需要app在后台常驻,用于实时上传一些健康信息数据,便于后台实时查看用户的健康状况。自从android7.0以上后台常驻实现越来越难,尤其是8.0及以上。关...

最近在做的项目中需要app在后台常驻,用于实时上传一些健康信息数据,便于后台实时查看用户的健康状况。自从android7.0以上后台常驻实现越来越难,尤其是8.0及以上。关于保活的文章比比皆是,但是效果并不理想,关于保活的方法也就常说的哪几种,重点在于怎么组合运用。最终实现效果为:用户不主动强制杀死的话,能够一直存活(小米,华为,vivo,oppo,三星)。其中三星s8,华为nova2s用户强制杀死也能存活。

项目结构

Android应用保活实践详解 

常见的保活方案

关于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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。