【问题标题】:Android 9.0: Not allowed to start service: app is in background.. after onResume()Android 9.0:不允许启动服务:应用程序在后台.. onResume() 之后
【发布时间】:2019-01-31 11:18:07
【问题描述】:

我有一个音乐播放器,它试图在 onResume() 中启动 Service 或 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。

【问题讨论】:

标签: android android-service android-9.0-pie


【解决方案1】:

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

在带有 Android 10、11、12 DP2 的 Android 模拟器上测试:

  • 启动在Activity.onResume()中启动服务的应用程序
  • 按 Home 按钮将活动的状态更改为已停止
  • 等到系统销毁服务(logcat日志会出现:“ActivityManager: Stopping service due to app idle”)
  • 打开安卓设置->系统->手势->系统导航
  • 将系统导航选项更改为其他选项
  • 应用程序将崩溃

我还通过以下步骤在 Android 问题跟踪器中创建了一个新问题:https://issuetracker.google.com/issues/183716536

【讨论】:

    【解决方案2】:

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

    如果服务的应用程序不是前台(即使服务本身是后台服务)我得到的错误:

    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)
    

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

    如果该服务是“后台服务”并且该服务的应用程序不在前台,则该服务可能会被终止 - 因此,如果您需要它继续存在,启动该服务是不够的。

    这似乎是从 Android 7 到 Android 8 变化的结果,在 Oreo 中他们开始限制一些东西..

    这一切都在 Background Execution Limits of Oreo 上的 Android 文档中进行了解释

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

    【讨论】:

      【解决方案3】:

      Google 提供了一种解决方法:

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

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

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

      // 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);
      

      【讨论】:

      • 正如 issuetracker 中所说:“活动将立即暂停,并最终在完全唤醒后再次恢复。”所以活动将再次恢复,我认为过程的重要性将是 IMPORTANCE_FOREGROUND。所以不需要处理。是真的吗?
      • 上面的“hack”对我有用(注意:在我的案例中,所有针对此问题报告的崩溃都在运行 Android 9+ 的三星平台上)。为了完整起见,在 'hack' 上方添加以下行来定义 activityManager:“ActivityManager activityManager = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE);”
      • @David 你在没有处理程序的情况下尝试过吗?
      • @XiWei 是的,我测试过了,没问题。只需在 onResume 中检查重要性后使用 startService。
      【解决方案4】:

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

      我们在 onResume 中启动服务并取消绑定 onPause。

      如何重现

      只需锁定您的设备,当屏幕上出现具有此逻辑的活动时,不要触摸 10-15 分钟。解锁屏幕后,应用会崩溃。

      如何解决

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

      示例(Kotlin):

      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)
          }
      }
      

      祝你好运!

      【讨论】:

      • 我进行了此更改,但运行 Android 9 的 SM-A750FN 手机仍会因相同的错误而崩溃。
      【解决方案5】:

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

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

      根据关闭问题的 Google 员工的说法,

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

      【讨论】:

        【解决方案6】:

        更新:这在 Prod 中对我们有用,但不是 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 触发,当它可能正在前往后台并立即打开/关闭时,它将清除一次性。

        【讨论】:

        • 对于 Oreo+ 的情况,这是否相当于:handler.postDelayed({context.startService(intent)}, 300) 和 stopped(),调用 handler.removeCallbacksAndMessages(null) ?
        • 我相信它的功能是一样的。始终可以通过添加一些额外的日志记录来测试它,并将时间设置为一两秒,以确认延迟/正确删除以及在您反复打开/关闭应用程序时触发的能力。
        • 任何人都可以使用 handler.postDelayed 确认上面发布的非 Rx 版本作品
        • @rinav 我会考虑使用谷歌官方推荐的解决方案。我已经取出了上面的生命周期逻辑并将它们的修复放在 else 分支而不是 Rx 中,它确实可以防止崩溃。唯一的问题是,它是否完全可靠,我还不知道,但一旦我们很快发布,我们就会知道。上面的 Rx 版本虽然大大减少了崩溃,但当我更换每个电话时,我仍然每月得到 3-4 个,而不是 400-500 个。不过好消息是,通过这个文件路由所有呼叫使得交换修复变得容易!
        【解决方案7】:

        也许 android.arch.lifecycle 可以用作这个 Android 9 错误的解决方法?

        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);
            }
        }
        

        I've found it here.

        【讨论】:

        • 生命周期观察者只是响应活动触发的事件。所以这一切实际上只是将startService() 调用移动到onStart()。不过,正如您所说,这可能是一个方便的解决方法。
        • 不幸的是,我看到了与Lifecycle.State.STARTED 相同的异常。我将尝试在onResume() 之后调用的Lifecycle.State.RESUMED 和@OnLifecycleEvent(Lifecycle.Event.ON_RESUME),看看是否能解决问题。
        • 感谢分享这个解决方法。你在使用 Lifecycle.State.RESUMED 和 @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) 时有什么积极的结果吗?
        • 我更新了我的应用程序以使用 State.RESUMED。几天来它工作正常,所以我希望这能解决问题,但我刚刚注意到 android 9 上再次崩溃:(
        • 是的,一点也不惊讶。同样,Arch Lifecycle 事件只是由底层 Fragment/Activity 的生命周期事件触发,所以这与将 startService 调用移动到 onStart() 或 onResume() 或任何地方没有什么不同,除非你考虑到非常微小的时间延误。听起来像将呼叫转移到 onStart() 是不是一种解决方法。
        猜你喜欢
        • 2020-05-19
        • 2019-11-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-06-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多