有java基础但看不懂Android

有java基础但看不懂Android导读 今天在用户的错误列表上看到这么个 bug 重现情景 由于我们的桌面小控件在 onUpdate 方法里用 Context startService 启动了 Service 当 app 的进程没有启动时 把桌面部件拉到 Launcher 桌面上就会报这个错误 Android 官网在 8 0 时的后台服务启动优化的一些措施 后台服务限制 处于空闲状态时 应用可以使用的后台服务存在限制

大家好,我是讯享网,很高兴认识大家。



导读

今天在用户的错误列表上看到这么个bug

重现情景

由于我们的桌面小控件在onUpdate()方法里用Context.startService()启动了Service.当app的进程没有启动时,把桌面部件拉到Launcher桌面上就会报这个错误.

Android官网在8.0时的后台服务启动优化的一些措施

后台服务限制:处于空闲状态时,应用可以使用的后台服务存在限制。 这些限制不适用于前台服务,因为前台服务更容易引起用户注意。 在 Android 8.0 之前,创建前台服务的方式通常是先创建一个后台服务,然后将该服务推到前台。 Android 8.0 有一项复杂功能;系统不允许后台应用创建后台服务。 因此,Android 8.0 引入了一种全新的方法,即 Context.startForegroundService(),以在前台启动新服务。 在系统创建服务后,应用有五秒的时间来调用该服务的 startForeground()方法以显示新服务的用户可见通知。 如果应用在此时间限制内未调用 startForeground(),则系统将停止服务并声明此应用为 ANR。

解决方案

我总结一下就是8.0后,如果一个处于后台的应用想要启动Service就必须调用Context.startForegroundService()并且5秒内在该Service内调用startForeground()

下面看看源码的变动情况

源码解析

首先是后台应用调用Context.startService()启动Service为什么会报错 启动Service的入口ContextImpl.startService()

讯享网

1处就是我们bug抛出异常的地方Not allowed to start service Intent... 我们先看看ActivityManager.getService().startService()的返回逻辑

 

启动Service会调用ActiveServices.startServiceLocked()

有java基础但看不懂Android
讯享网

这里的fgRequired是从ContextImpl.startServiceCommon(fgRequired:false)传进来的,为false. 2标记处是不是又看到相关bug信息了 "app is in background uid...",于是我们看看allowed返回值mAm.getAppStartModeLocked()

 

allowed的返回值就是startMode.这里alwaysRestrict是传入的参数false,这里的uidRec由于应用进程都未启动,于是uidRec.idle为true表示空闲进程,所以我们直接看appServicesRestrictedInBackgroundLocked()

 

这个方法会判断是否是Persistent app,白名单,电量白名单应用,很显然普通app都不是,于是进入appRestrictedInBackgroundLocked()看看

 

这里的packageTargetSdk刚好是O,所以返回ActivityManager.APP_START_MODE_DELAYED_RIGID了.由于返回值不是ActivityManager.APP_START_MODE_NORMAL.于是就return new ComponentName("?", "app is in background uid " + uidRec);然后就出现了开头的异常.

下面看下Context.startForegroundService启动Service的逻辑 入口依旧为ContextImpl.startForegroundService()

 

这里与startService的区别就在于传入的fgRequired为true.于是一路 ContextImpl.startServiceCommon()-->ActivityManagerService.startService()-->ActiveServices.startServiceLocked(),由于fgRequired为true,就跳过刚才那段逻辑下面就是正常的Service启动流程了. 那么还有一个问题,为什么还需要在5秒内调用Service.startForeground()呢? 在启动Service的过程中会调用到ActiveServices.bringUpServiceLocked()方法,然后会调用ActiveServices.sendServiceArgsLocked()

 

在3处会调用scheduleServiceForegroundTransitionTimeoutLocked()作用就是发送一个延时5秒的message

 

看下这个消息的处理

 

又来到ActiveServices

 

这里就是调用stopServiceLocked(r)把service关掉了.那么Service.startForeground()一定会有代码取消这个消息,来看:

 

mActivityManager就是调用AMS

 

来看setServiceForegroundInnerLocked()

 

这里就removeMessages(SERVICE_FOREGROUND_TIMEOUT_MSG)取消这个message了.

小讯
上一篇 2024-12-26 09:18
下一篇 2024-12-30 11:36

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/10533.html