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()
中启动 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 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
Android 26无法启动接收器com.google.android.gcm.GCMBroadcastReceiver:java.lang.IllegalStateException:不允许启动服务