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

Posted

技术标签:

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

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

@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 UidRecord6a4a9c6 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。

【问题讨论】:

我没有给你一个好的答案,但我可以确认我们也看到了这个。我们从未在内部复制过它,但同样我们在 onResume() 中启动服务时会看到它。我怀疑这是 Android P 中的一个错误。 好的,很高兴不仅仅是我。已报告此问题:issuetracker.google.com/issues/113122354 Android 8.0: java.lang.IllegalStateException: Not allowed to start service Intent 的可能重复项。后来有人问过,但有答案。 另外,面向 Android 9 或更高版本并使用前台服务的应用必须请求 FOREGROUND_SERVICE 权限。这是一个正常的权限,因此系统会自动将其授予请求的应用程序。来自developer.android.com/about/versions/pie/android-9.0-changes-28 @iaindownie 是的,它似乎奏效了,它已经投入生产了一段时间,并且我尚未修复的新实例(当时不知道)开始出现因为它走得更远了。 【参考方案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 问题跟踪器中标记为“已修复”:

https://issuetracker.google.com/issues/110237673 https://issuetracker.google.com/issues/113122354

据推测,该修复程序将在 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()不是一种解决方法。

以上是关于Android 9.0:不允许启动服务:应用程序在后台.. onResume() 之后的主要内容,如果未能解决你的问题,请参考以下文章

Android 8.0:java.lang.IllegalStateException:不允许启动服务 Intent

Android : 为系统服务添加 SELinux 权限 (Android 9.0)

Andoid 9.0 程序更新后不能弹出“打开完成”界面,或点击“打开”不能启动程序

多进程中的 Android Pie (9.0) WebView

无法恢复活动:不允许启动服务 Intent,应用程序在后台

Android 26无法启动接收器com.google.android.gcm.GCMBroadcastReceiver:java.lang.IllegalStateException:不允许启动服务