在一加手机上,用户升级了新版8.0的系统,用户将app切到后台,过一会儿就弹出“xxx app 已停止运行”的弹窗。
通过定位分析,发现下面俩前置条件
- 8.0系统杀服务杀的很频繁
- 为了保活,我们使用了俩Service互保的方式
马上跑了26的模拟器,果然复现,日志如下:
main
Process: com.icourt.alpha.service.LocalService, PID: 4343
java.lang.IllegalStateException: Not allowed to start service Intent { cmp=com.icourt.alpha/.service.LocalService }: app is in background uid UidRecord{81da92c u0a91 SVC bg:+1m0s13ms idle procs:4 seq(0,0,0)}
at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1505)
at android.app.ContextImpl.startService(ContextImpl.java:1461)
at android.content.ContextWrapper.startService(ContextWrapper.java:644)
at com.icourt.alpha.service.RemoteService$RemoteServiceConnection.onServiceDisconnected(RemoteService.java:100)
at android.app.LoadedApk$ServiceDispatcher.doConnected(LoadedApk.java:1627)
at android.app.LoadedApk$ServiceDispatcher$RunConnection.run(LoadedApk.java:1663)
at android.os.Handler.handleCallback(Handler.java:789)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6541)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
我查阅了android官网,有如下一段描述:
Android 8.0 还对特定函数做出了以下变更:
- 如果针对 Android 8.0 的应用尝试在不允许其创建后台服务的情况下使用
startService()
函数,则该函数将引发一个IllegalStateException
。- 新的
Context.startForegroundService()
函数将启动一个前台服务。现在,即使应用在后台运行,系统也允许其调用Context.startForegroundService()
。不过,应用必须在创建服务后的五秒内调用该服务的startForeground()
函数。
解决方法就很简单了,把Service互启的逻辑块改为:
if (Build.VERSION.SDK_INT >= 26) {
context.startForegroundService(intent);
} else {
// Pre-O behavior.
context.startService(intent);
}
有个简写:ContextCompat.startForegroundService(context, intent)
因为我不想让用户在控制面板看到通知栏,我没有在创建服务后的五秒内调用startForeground()
。我马上试了一下,果然没有了“已停止运行”,但是日志爆出了另一个ANR的错误,虽然不是在主线程,也没有弹窗,但毕竟是个错误,我想一探究竟。
回到官方的描述:在被启动的Service创建服务后的五秒内调用startForground(0, new Notification())
,如果不调用或调用时间超过5秒会抛出一个ANR。
果不其然,我只有调用了,这样更好,有前台进程,系统想杀都杀不掉了。
但仔细看一下官方文档,你会发现,官方推荐使用JobScheduler
,这是api25的新特性。