Android 9.0:不允许启动服务:应用程序在后台.. onResume()之后


73

我有一个音乐播放器,试图启动其中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上。


7
我没有一个很好的答案,但是我可以确认我们也看到了这一点。我们从未在内部复制过它,但是类似地,在onResume()中启动服务时我们也看到了它。我怀疑这是Android中P.的错误
凯文科波克

9
好吧,很高兴不仅仅是我。已报告此问题:issuetracker.google.com/issues/113122354
蒂姆·


3
此外,以Android 9或更高版本为目标并使用前台服务的应用程序必须请求FOREGROUND_SERVICE权限。这是正常权限,因此系统会自动将其授予请求的应用程序。来自developer.android.com/about/versions/pie/android-9.0-changes-28
访问被拒

2
@iaindownie是的,它似乎已经起作用了,它已经投入生产了很短的时间,自从它进一步发展以来,我还没有修复(当时不知道)的新实例开始出现。
Ben987654

Answers:


12

Google提供了一种解决方法:

该问题已在以后的Android版本中解决。

有一种解决方法可以避免应用程序崩溃。如果重要性级别低于ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND,应用程序可以通过调用ActivityManager.getRunningAppProcesses()获取Activity.onResume()中的进程状态,并避免启动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);

4
如issuetracker中所述:“活动将立即暂停,并在完全唤醒后最终又恢复。” 因此,我认为活动将再次恢复,过程的重要性将为IMPORTANCE_FOREGROUND。所以不需要处理。是真的吗
大卫

2
上面的“ hack”对我有用(请注意:在我的案例中,针对此问题报告的所有崩溃均在运行Android 9+的三星平台上进行)。为了完整起见,在“ hack”上方添加以下行以定义activityManager:“ ActivityManager activityManager =(ActivityManager)this.getSystemService(Context.ACTIVITY_SERVICE);”
gOnZo

@David在没有处理程序的情况下尝试了吗?
席薇

@XiWei是的,我已经测试过了,没关系。在onResume中检查重要性之后,只需使用startService即可。
戴维

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的触发,从而取消了该触发,它将清除一次性对象。


1
对于Oreo +案例,这是否等同于:handler.postDelayed({context.startService(intent)}, 300)和在中stopped(),调用handler.removeCallbacksAndMessages(null)
卡门,

1
我相信这将起到相同的作用。可以随时通过添加一些额外的日志记录来进行测试,并将时间设置为一到两秒,以确认延迟/正确删除以及在重复打开/关闭应用程序时触发的能力。
Ben987654 '19

任何人都可以使用
handler.postDelayed

@rinav我现在会考虑使用Google官方推荐的解决方案。我已取出上面的生命周期逻辑,并将其修复程序放在else分支而不是Rx中,它确实可以防止崩溃。唯一的问题是,它是否完全可靠,我尚不知道,但是一旦我们很快发布,我们就会发现。上面的Rx版本虽然大大减少了崩溃,但是当我替换每个呼叫时,我仍然每个月得到3-4个,而不是400-500个。好消息是,通过该文件路由所有呼叫使交换修补程序变得很容易!
Ben987654

4

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

大概该修复程序将在Android Q版本之一中发布。

根据解决此问题的Google员工的说法,

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


3

我们的团队面临同样的问题。我的日历显示2019年4月5日,但问题仍在我的Samsung 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)
    }
}

祝好运!


我进行了此更改,但Android 9上的SM-A750FN手机仍然因相同的错误而崩溃。
Vajk Hermecz

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文档中进行了说明 关于Oreo后台执行限制,

文档中的建议是将您的逻辑迁移到计划的作业,或者将服务设为前台服务-在此处将直观地表明该服务正在运行。


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

我在这里找到了。


1
生命周期观察者仅响应活动触发的事件。因此,所有这些实际上等于将startService()呼叫移至onStart()。正如您所说,这可能是一个方便的解决方法。
蒂姆·马尔塞德(Dim Malseed)'18年

1
不幸的是,我看到与相同的异常Lifecycle.State.STARTED。我要尝试一下Lifecycle.State.RESUMED@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)然后再调用它onResume(),看看是否能解决问题。
MarcinLe

感谢您分享此解决方法。使用Lifecycle.State.RESUMED和@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)时,您有任何积极的结果吗?
Renso Lohuis '18 -10-29

我更新了我的应用程序以使用State.RESUMED。几天来它运行良好,所以我希望这可以解决问题,但是我刚刚注意到android 9再次崩溃:(
MarcinLe

是的,一点也不惊讶。同样,Arch生命周期事件只是由基础Fragment / Activity的生命周期事件触发的,因此,这与仅将startService呼叫移入onStart()或移入onResume()任何地方没有什么不同,除非您考虑到非常小的计时延迟。如呼叫转移到声音onStart()不是一个解决办法。
蒂姆·马尔塞德
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.