Android 9.0: 不允许启动 service: app 在后台. . after onResume()

我有一个音乐播放器,它试图启动一个 ServiceonResume()Activity。为了清晰起见,我删除了几行代码,但是代码是有效的:

@Override
protected void onResume() {
super.onResume();


startService(new Intent(this, MusicService.class));
}

根据崩溃日志,这会在一些运行 Android P 的设备上引发一个异常:

Caused by java.lang.IllegalStateException: Not allowed to start service Intent { cmp=another.music.player/com.simplecity.amp_library.playback.MusicService }: app is in background uid UidRecord{6a4a9c6 u0a143 TPSL bg:+3m25s199ms idle change:cached procs:1 seq(1283,1283,1283)}
at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1577)
at android.app.ContextImpl.startService(ContextImpl.java:1532)
at android.content.ContextWrapper.startService(ContextWrapper.java:664)
at android.content.ContextWrapper.startService(ContextWrapper.java:664)
at com.simplecity.amp_library.utils.MusicServiceConnectionUtils.bindToService(SourceFile:36)
at com.simplecity.amp_library.ui.activities.BaseActivity.bindService(SourceFile:129)
at com.simplecity.amp_library.ui.activities.BaseActivity.onResume(SourceFile:96)

怎么可能我的应用程序在后台,紧接着 onResume()(和 super.onResume())被调用?

这对我来说毫无意义。这可能是平台错误吗?所有受此次崩溃影响的3500多名用户都在 Android P 上。

38872 次浏览

也许 Android.arch.life 周期可以作为这个 Android 9 bug 的解决方案?

public class MyActivity extends Activity implements LifecycleObserver {


protected void onResume() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (ProcessLifecycleOwner.get().getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)) {
startService(intent);
} else {
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
}
} else {
startService(intent);
}
}




@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
void onEnterForeground() {
startService(intent);
ProcessLifecycleOwner.get().getLifecycle().removeObserver(this);
}
}

我在这里找到的。

更新: 这是为我们工作的 Prod,但它不是100% 。在过去的一个半月里,我收到了一份坠机报告,而如果不是这份报告的话,可能会有超过100份。在这个问题得到妥善解决之前,这似乎是我们目前最好的选择。如果我把时间提高到300以上,也许就不会发生一次车祸了?

我们现在正在测试,目前看来是有效的。将更新,因为我们看到更多的结果

class ResumingServiceManager(val lifecycle: Lifecycle) : LifecycleObserver {


init {
lifecycle.addObserver(this)
}


val disposable: CompositeDisposable = CompositeDisposable()


fun startService(context: Context, intent: Intent) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
context.startService(intent)
} else {
Single.just(true)
.delaySubscription(300, TimeUnit.MILLISECONDS)
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribeBy(
onSuccess = {
context.startService(intent)
}


).addTo(disposable)
}
}


@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun stopped() {
disposable.clear()
}


@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun destroy() {
lifecycle.removeObserver(this)
}
}

onCreate()中初始化它,然后在任何时候你想在 onResume 中启动一个服务,只需调用 resumingServiceManager.startService(this, intent)

它的生命周期感知,所以它会清除一次性的,如果它暂停取消 onSuccess 触发,当它可能是在后台的方式立即打开/关闭。

这在 Android 问题追踪器中被标记为“修复”:

据推测,该修复程序将在 Android Q 版本中发布。

根据结案的谷歌人所说,

有一个解决方案可以避免应用程序崩溃。应用程序可以通过调用 ActivityManager.getRunningAppProcesses()获得 Activity.onResume()中的进程状态,如果重要性级别低于 ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND,则避免启动服务。如果设备没有完全清醒,活动将立即暂停,并最终在其完全清醒后再次恢复。

谷歌(Google)提供了一个变通方案:

这个问题已经在未来的 Android 版本中得到解决。

有一个解决方案可以避免应用程序崩溃 调用 Activity.onResume ()中的进程状态 GetRunningAppProcses () ,并避免在以下情况下启动 Service 重要性水平低于 如果 设备尚未完全清醒,活动将立即暂停和 最终在它完全清醒之后再次恢复。

所以我认为它应该是这样的:

// hack for https://issuetracker.google.com/issues/113122354
List<ActivityManager.RunningAppProcessInfo> runningAppProcesses = activityManager.getRunningAppProcesses();
if (runningAppProcesses != null) {
int importance = runningAppProcesses.get(0).importance;
// higher importance has lower number (?)
if (importance <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND)
URLPlayerService.startActionBroadcastServiceData(PlayerActivity.this);
}

我已经使用处理程序作为一个解决方案,它的工作相当不错,但不是100% :

// hack for https://issuetracker.google.com/issues/113122354
handler.postDelayed(() -> URLPlayerService.startService(PlayerActivity.this),200);

我们的团队面临着同样的问题。我的日历显示2019年4月5日,但问题仍然在我的三星 Galaxy S9 + ,android 9.0(One UI)上重现

我们在恢复时开始服务,在暂停时解除绑定。

如何繁殖

只要锁定你的设备,当这个逻辑的活动是在屏幕上,不要触摸10-15分钟。解锁屏幕后,应用程序会崩溃。

如何解决

我们找到了实际可行的解决方案,从 android.os.Handler.post (...)开始,在 android.os.Handler.post (...)中启动你的服务

例子(科特林) :

override fun onResume() {
super.onResume()
Handler().post {
val serviceIntent = Intent(activity, SomeService::class.java)
activity?.startService(serviceIntent)
activity?.bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE)
}
}

祝你好运!

似乎错误消息是模棱两可的,我发现消息的最后一部分是指服务所在的应用程序,而不是试图启动服务并绑定到它的应用程序。

如果服务的应用程序没有前台接地(即使服务本身是一个后台服务) ,我会得到这样的错误:

W/System.err: java.lang.IllegalStateException: Not allowed to start service Intent { cmp=com.mycompany.myserviceapp/.MyService }: app is in background uid null
at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1505)
at android.app.ContextImpl.startService(ContextImpl.java:1461)
at android.content.ContextWrapper.startService(ContextWrapper.java:644)

现在,如果我运行具有该服务的应用程序,那么在启动尝试启动该服务的“客户端”应用程序之前,该应用程序处于前台,那么服务启动时一切正常。

如果该服务是一个“后台服务”,而该服务的应用程序不在前台,该服务可能会被终止-因此,如果你需要它留在身边,启动该服务是不够的。

这似乎是从安卓7到安卓8的变化的结果,在奥利奥中,他们开始限制一些东西。.

这些都在 奥利奥的背景执行限制的 Android 文档中解释过

文档中的建议是将您的逻辑迁移到计划作业,或者将服务设置为前台服务——在前台服务中将有一个可视的指示,表明服务正在运行。

我找到了重现问题的方法。

使用 Android 10,11,12 DP2在 Android 模拟器上进行测试:

  • 启动在 Activity.onResume()中启动服务的应用程序
  • 回家按钮将活动状态改为 停止了
  • 等待直到系统销毁服务(logcat 日志将出现: “ ActivityManager: 由于应用程序空闲而停止服务”)
  • 打开 Android 设置-> 系统-> 手势-> 系统导航
  • 将系统导航选项更改为其他选项
  • 应用程序会崩溃

我还用这些步骤在 Android 问题跟踪器中创建了一个新问题: https://issuetracker.google.com/issues/183716536

我已经找到了一个解决方案,即使用流程作用域,并确保在日志逻辑中不包括作用域取消异常。 像这样:

with(ProcessLifecycleOwner.get()) {
lifecycleScope.launch {
lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) {
try {
context.startService(context, Service::class.java)
} catch (ex: CancellationException) {
// app minimized, scope cancelled, do not log as error
} catch (ex: IllegalStateException) {
logToFirebase(ex)
}
}
}
}

在本文 https://medium.com/@lepicekmichal/android-background-service-without-hiccup-501e4479110f中有更详细的介绍