现象
android o 版本 (8.0) 及以上版本, 当应用处于后台时执行 startService 时, 会抛出如下异常:
Caused by: java.lang.IllegalStateException: Not allowed to start service ... app is in background uid UidRecord ...
初步理解为由于 app 处于后台时 startServic 不被允许
原因分析
从 android O 版本开始, google 为了控制资源使用增加了两项后台限制:
后台服务
广播
其中对于后台服务的限制, 指的是如果应用处于后台, 则不允许直接使用 startService.
那什么是后台应用? 后台的对立面是前台, google 对于前台的定义如下:
具有可见的 activity(不管该 activity 已启动还是已暂停)
具有前台服务
有关联的前台应用(如壁纸, 通知侦听器等)
具体可参考官方链接:
https://developer.android.com/about/versions/oreo/background
解决方案
既然我们使用了后台服务, 必然是一些无需用户感知的场景, 故考虑替换为前台服务的可能性不大;
从 google 对前台应用的解释入手, 可以通过制造前台场景的方式使自己的应用处于前台;
官方还提及了 JobScheduler 替代后台服务的方案, 可以根据的业务场景选择是否使用;
startService 的方式也不需要完全放弃. 为了适配 8.0 手机, 需要先判断应用是否在前台再决定是否使用 startService;
判断应用是否在前台的方式很多, 主要原理有两种:
通过 AM 判断应用前台 activity 个数
通过 actvitiy 生命周期回调计数 (Activity 回调, ActivityLifecycleCallbacks) 判断是否有前台界面
实现的方式很多, 自行 google
设置 targetSdk <26 也可以实现该新特性规避.
源码分析
startService 大致流程如下:
- ContextImpl#startServce ->
- ContextImpl#startServceCommon ->
- AMS#startService ->
- ActiveServices#startServiceLocked ->
- AMS#checkAllowBackgroundLocked
- 8.0.0
- ContextImpl.java#startServiceCommon
红框中为日志信息的来源
ActivityServices.java#startServiceLocked
- AMS # getAppStartModeLocked ->
- AMS # appServicesRestrictedInBackgroundLocked ->
- AMS # appRestrictedInBackgroundLocked
来源: https://juejin.im/post/5b8b6f476fb9a019f0071379