78

我有一个音乐播放器,它试图启动一个Service. 为了清楚起见,我删除了几行,但代码实际上是:onResume()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()(and ) 之后立即在后台运行?super.onResume()

这对我来说没有任何意义。这可能是平台错误吗?受此崩溃影响的所有 3500 多名用户都使用 Android P。

4

7 回答 7

15

谷歌有一个解决方法:

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

有一种解决方法可以避免应用程序崩溃。应用程序可以通过调用 ActivityManager.getRunningAppProcesses() 获取 Activity.onResume() 中的进程状态,如果重要性级别低于 ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND,则可以避免启动 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-03-27T11:23:12.150 回答
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 触发,当它可能正在前往后台并立即打开/关闭时,它将清除一次性。

于 2018-11-10T00:49:59.537 回答
5

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

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

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

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

于 2019-03-26T10:05:07.493 回答
4

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

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

如何重现

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

怎么修

我们找到了真正有效的解决方案。从 android 8+ 开始,在 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)
    }
}

祝你好运!

于 2019-04-05T12:50:41.500 回答
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

于 2021-03-25T20:59:49.867 回答
0

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

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

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 中他们开始限制一些东西。

这一切都在 Android docs on the Background Execution Limits of Oreo中进行了解释

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

于 2020-11-13T04:13:45.110 回答
-3

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

我在这里找到了。

于 2018-10-20T17:54:15.530 回答