如何解决Service的启动稳定性问题

199 阅读1分钟

背景

我们项目,常有startofrgroundsercie启动失败,bug率也比较高

探究得知,有几个原因会导致service启动失败

  1. 系统的运行条件不确定,binder访问有时非常频繁,此时服务启动会失败
  2. service是单独的进程,进程创建耗时,此时服务启动可能失败
  3. ard8之后都需要startForgroundService了。在该方法执行后,如果10ms(<ard13)/30ms(ard13),服务启动会失败,会ANR

思路

上述1,2,开发者是解决不了的。我们可以从3入手。解决思路是,快速执行startForground, 或者让startForgroundService延后执行

解决方案

探究了下service源码,service内部有两处可能的anr。如下图 ANR炸弹

  1. 第一处是在oncreate 之前send了一个delay msg, 在oncreate之后移除该msg。这其实触发anr的思路,如果在oncreate执行前,dealy msg执行了就会直接报anr。
  2. 第二处同理,未启动服务时,在oncreate之前send,setForground之后remove msg。

所以引出了两个解决对策

  1. setforground尽量放到oncreate最前面,不要放到onstartcommand里
  2. 我们可以用bindservice先启动服务,然后调用startForgroundService,这样就没有anr的检查了。因为已经在oncreate函数执行了startForground.

解决代码如下

val connection = object : ServiceConnection {
    override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
        context.startForegroundService(serverIntent)
        context.unbindService(this)
    }

    override fun onServiceDisconnected(name: ComponentName?) {
    }

    override fun onNullBinding(name: ComponentName?) {
        super.onNullBinding(name)
        context.startForegroundService(serverIntent)
        context.unbindService(this)
    }
}
context.bindService(serverIntent, connection, Context.BIND_AUTO_CREATE)

大家通过看源码,可以放心,bindservice -> startForegroundService 是没有任何问题的。bringUpServiceLock在第一句就返回了

image.png