在Android 12(SDK 31)中遇到android.app.ForegroundServiceStartNotAllowedException问题

34

我将我的应用程序的targetSdkVersioncompileSdkVersion升级到SDK 31,并在后台更新小部件的服务中开始收到以下崩溃。

转化为:

我把我的应用程序的targetSdkVersioncompileSdkVersion升级到了SDK 31,然后在后台更新小部件的服务中出现了以下崩溃。

java.lang.RuntimeException: 
  at android.app.ActivityThread.handleReceiver (ActivityThread.java:4321)
  at android.app.ActivityThread.access$1600 (ActivityThread.java:247)
  at android.app.ActivityThread$H.handleMessage (ActivityThread.java:2068)
  at android.os.Handler.dispatchMessage (Handler.java:106)
  at android.os.Looper.loopOnce (Looper.java:201)
  at android.os.Looper.loop (Looper.java:288)
  at android.app.ActivityThread.main (ActivityThread.java:7842)
  at java.lang.reflect.Method.invoke (Native Method)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:548)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1003)
Caused by: android.app.ForegroundServiceStartNotAllowedException: 
  at android.app.ForegroundServiceStartNotAllowedException$1.createFromParcel (ForegroundServiceStartNotAllowedException.java:54)
  at android.app.ForegroundServiceStartNotAllowedException$1.createFromParcel (ForegroundServiceStartNotAllowedException.java:50)
  at android.os.Parcel.readParcelable (Parcel.java:3333)
  at android.os.Parcel.createExceptionOrNull (Parcel.java:2420)
  at android.os.Parcel.createException (Parcel.java:2409)
  at android.os.Parcel.readException (Parcel.java:2392)
  at android.os.Parcel.readException (Parcel.java:2334)
  at android.app.IActivityManager$Stub$Proxy.startService (IActivityManager.java:5971)
  at android.app.ContextImpl.startServiceCommon (ContextImpl.java:1847)
  at android.app.ContextImpl.startForegroundService (ContextImpl.java:1823)
  at android.content.ContextWrapper.startForegroundService (ContextWrapper.java:779)
  at android.content.ContextWrapper.startForegroundService (ContextWrapper.java:779)
  at com.mypackage.appname.ui.widget.widget_package.WidgetClassName.onUpdate (WidgetClassName.java:48)
  at android.appwidget.AppWidgetProvider.onReceive (AppWidgetProvider.java:66)
  at com.mypackage.appname.ui.widget.widget_package.WidgetClassName.onReceive (WidgetClassName.java)
  at android.app.ActivityThread.handleReceiver (ActivityThread.java:4312)
  at android.app.ActivityThread.access$1600 (ActivityThread.java:247)
  at android.app.ActivityThread$H.handleMessage (ActivityThread.java:2068)
  at android.os.Handler.dispatchMessage (Handler.java:106)
  at android.os.Looper.loopOnce (Looper.java:201)
  at android.os.Looper.loop (Looper.java:288)
  at android.app.ActivityThread.main (ActivityThread.java:7842)
  at java.lang.reflect.Method.invoke (Native Method)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:548)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1003)
Caused by: android.os.RemoteException: 
  at com.android.server.am.ActiveServices.startServiceLocked (ActiveServices.java:691)
  at com.android.server.am.ActiveServices.startServiceLocked (ActiveServices.java:616)
  at com.android.server.am.ActivityManagerService.startService (ActivityManagerService.java:11839)
  at android.app.IActivityManager$Stub.onTransact (IActivityManager.java:2519)
  at com.android.server.am.ActivityManagerService.onTransact (ActivityManagerService.java:2498)

另外,如果你正在使用类似 Firebase Crashlytics 的东西,那么你的堆栈跟踪必须像这样:

->

Caused by android.app.ForegroundServiceStartNotAllowedException: startForegroundService() not allowed due to mAllowStartForeground false: service com.mypackage.appname/.ui.widget.widget_package.MyForegroundServiceName
       at android.app.ForegroundServiceStartNotAllowedException$1.createFromParcel(ForegroundServiceStartNotAllowedException.java:54)
       at android.app.ForegroundServiceStartNotAllowedException$1.createFromParcel(ForegroundServiceStartNotAllowedException.java:50)
       at android.os.Parcel.readParcelable(Parcel.java:3333)
       at android.os.Parcel.createExceptionOrNull(Parcel.java:2420)
       at android.os.Parcel.createException(Parcel.java:2409)
       at android.os.Parcel.readException(Parcel.java:2392)
       at android.os.Parcel.readException(Parcel.java:2334)
       at android.app.IActivityManager$Stub$Proxy.startService(IActivityManager.java:5971)
       at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1847)
       at android.app.ContextImpl.startForegroundService(ContextImpl.java:1823)
       at android.content.ContextWrapper.startForegroundService(ContextWrapper.java:779)
       at android.content.ContextWrapper.startForegroundService(ContextWrapper.java:779)
       at com.mypackage.appname.ui.widget.widget_package.WidgetClassName.onUpdate(WidgetClassName.java:48)
       at android.appwidget.AppWidgetProvider.onReceive(AppWidgetProvider.java:66)
       at com.mypackage.appname.ui.widget.widget_package.WidgetClassName.onReceive(WidgetClassName.java)
       at android.app.ActivityThread.handleReceiver(ActivityThread.java:4312)
       at android.app.ActivityThread.access$1600(ActivityThread.java:247)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2068)
       at android.os.Handler.dispatchMessage(Handler.java:106)
       at android.os.Looper.loopOnce(Looper.java:201)
       at android.os.Looper.loop(Looper.java:288)
       at android.app.ActivityThread.main(ActivityThread.java:7842)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)

我正在添加复现此问题的方法以及解决此问题的办法,因为当我在 StackOverflow 上搜索时,并没有找到任何关于此的文档。

3个回答

37

如何复现崩溃问题

步骤1:将targetSdkVersioncompileSdkVersion更新至SDK 31。

步骤2:尝试在应用程序处于后台时运行任何前台服务。在我的情况下,是小部件的onUpdate方法在updatePeriodMillis时间后被调用,这将启动一个前台服务,该服务通过从互联网获取适当的信息来更新数据。

请注意:Android 8.0中添加的后台执行限制与此问题无关。这个限制/异常是在Android 12/SDK 31中添加的-参考资料


什么是这个异常,它为什么会被添加?

针对Android 12(API级别31)或更高版本的应用程序不能在后台运行时启动前台服务,除非是一些特殊情况。如果应用程序尝试在后台运行时启动前台服务,并且前台服务没有满足特例之一,系统将抛出一个ForegroundServiceStartNotAllowedException异常。

这些特殊情况包括:

  • 您的应用程序从用户可见状态(例如活动)转换而来。

  • 您的应用程序可以在后台启动活动,但如果应用程序有在现有任务的返回堆栈中的活动,则除外。

  • 您的应用程序使用Firebase Cloud Messaging收到高优先级消息。

  • 用户在与您的应用程序相关的UI元素上执行操作。例如,他们可能与气泡、通知、小部件或活动进行交互。

  • 您的应用程序调用精确的闹钟以完成用户请求的操作。

  • 您的应用程序是设备的当前输入法。

  • 您的应用程序接收与地理围栏或活动识别转换相关的事件。

  • 设备重新启动并在广播接收器中接收ACTION_BOOT_COMPLETED、ACTION_LOCKED_BOOT_COMPLETED或ACTION_MY_PACKAGE_REPLACED意图动作后。

  • 您的应用程序接收广播接收器中的ACTION_TIMEZONE_CHANGED、ACTION_TIME_CHANGED或ACTION_LOCALE_CHANGED意图动作。

  • 您的应用程序接收需要BLUETOOTH_CONNECT或BLUETOOTH_SCAN权限的蓝牙广播。

  • 具有某些系统角色或权限的应用程序,例如设备所有者和配置文件所有者。

  • 您的应用使用了 Companion Device Manager,并声明了 REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND 权限或 REQUEST_COMPANION_RUN_IN_BACKGROUND 权限。尽可能使用 REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND。

  • 用户关闭了您的应用的电池优化功能。您可以通过将用户发送到您的应用在系统设置中的“应用信息”页面来帮助用户找到此选项。为此,请调用一个包含 ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS 意图操作的意图。


可能的解决方案

解决方案 1

这在 Play Store 中起作用一段时间,直到谷歌强制升级到 API level 31 为止。

当前,从2021年11月开始,所有应用程序必须针对API Level 30及以上进行目标定向。因此,如果您的应用程序使用API Level 31,则将 `compileSdkVersion` 和 `targetSdkVersion` 降级至API Level 30 可以解决问题(至少暂时解决)。

解决方案 2

针对时间敏感的工作

如果您正在使用前台服务来处理时间敏感的工作,请在精确的闹钟内启动前台服务。有关更多信息,请查看文档 -> 设置精确的闹钟

针对时间不敏感/加急的工作

这是我最终用于我的应用程序的解决方案。使用WorkManager来安排和启动后台工作。在此处查看更多文档信息 -> 安排加速工作

您可以在此处了解有关WorkManager的更多信息 -> WorkManager

WorkManager示例的 Github 存储库 -> WorkManager 示例

我特别添加了这个答案,因为搜索此异常并没有带出任何资源来了解服务在 Android 12 上的行为方式发生变化的原因。这一切都在 Google 的文档中提到,并且请记住从文档中检查行为变更。

与此更改相关的所有内容都可以在此处找到 -> Android 12 行为变更,尤其是在前台服务启动限制中。


1
我在Pixel 6 - Android 12设备上遇到了同样的问题。即使我的应用程序通过Activity Recognition API启动前台服务,Crashlytics仍会记录这些异常。 - StevenTB
1
@user924 不,他们在Android 12中添加了这个新限制。您不能在用户未在您的应用程序UI上执行任何操作的情况下从后台启动服务。 - Vedprakash Wagh
1
@StevenTB 我和你一样遇到了同样的问题。你有找到解决方案吗? - user1443721
1
@user1443721 我不得不要求用户禁用电池优化,以避免背景启动限制:https://developer.android.com/guide/components/foreground-services#background-start-restriction-exemptions - StevenTB
1
谢谢!电池优化是另一个例外。不知道为什么活动识别转换的例外不起作用。 - user1443721
显示剩余10条评论

14

如果您的应用程序是一个MediaPlayer(即使用MediaBrowseService),并且您遇到了ForegroundServiceStartNotAllowedException崩溃,则可能需要:

  1. 更新您的清单以指定您的服务用于媒体播放,使用android:foregroundServiceType="mediaPlayback"Service声明中

  2. 在调用Serivce.startForeground方法时,包括ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK参数

另外,请注意,如果您正在使用ExoPlayer,则请参阅Android团队在此处提供的建议,这些建议可能有助于跟踪由于抛出ForegroundServiceStartNotAllowedException异常而导致崩溃的原因。

https://github.com/androidx/media/issues/111#issuecomment-1406466363

此外,在https://www.hellsoft.se/your-guide-to-foreground-services-on-andorid/上有一些非常实用且最新(至API31)的关于前台服务的信息。

1
谢谢您的回复。我尝试了一下,但不幸的是它没有起作用。我在这里有一个类似的问题:https://dev59.com/GlEG5IYBdhLWcg3wM2du,除了我似乎是在前台时遇到了这个错误。 - brettywhite
3
这完全没有帮助,至少在三星设备上不行。当服务由类似KEYCODE_MEDIA_PLAY的东西启动时,即使应用程序完全在后台运行,它仍会崩溃。 - slezadav
添加服务类型并不能解决问题! - Ahmed Garhy

-2

在创建通知时使用.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE),并且使用NotificationManager.IMPORTANCE_MAX


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接