在Android 12中获取android.app.ForegroundServiceStartNotAllowedException(SDK31)

2022-02-21 00:00:00 service android kotlin android-studio java

我将我的应用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时没有找到任何有关此问题的文档。


解决方案

如何重现崩溃

步骤1.将targetSdkVersioncompileSdkVersion更新为SDK 31。

步骤2.尝试在应用程序处于后台时运行任何前台服务。在我的例子中,小部件的onUpdate方法是在updatePeriodMillis时间之后调用的,它将启动一个前台服务,该服务通过从Internet获取适当的信息来更新数据。

记住:Android 8.0新增的后台执行限制与此问题无关。此限制/例外是在Android 12/SDK 31-Source中添加的。


此例外是什么,为什么添加它?

面向Android 12(API级别31)或更高版本的应用程序在后台运行时不能启动前台服务,但少数特殊情况除外。如果应用程序在后台运行时尝试启动前台服务,而前台服务不能满足其中一个异常情况,则系统会抛出ForegroundServiceStartNotAllowedException

这些special cases是:

  • 您的应用从用户可见状态(如活动)转换。

  • 您的应用程序可以从后台启动活动,但应用程序在现有任务的后栈中有活动的情况除外。

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

  • 用户对与您的应用程序相关的UI元素执行操作。例如,它们可能与泡沫、通知、小工具或活动交互。

  • 您的应用调用准确的警报来完成用户请求的操作。

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

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

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

  • 您的应用程序在广播接收器中接收action_timezone_Changed、action_time_Changed或action_locale_Changed意图操作。

  • 您的应用收到需要Bluetooth_CONNECT或Bluetooth_SCAN权限的蓝牙广播。

  • 具有特定系统角色或权限的应用,如设备所有者和配置文件所有者。

  • 您的应用程序使用"随行设备管理器"并声明REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND权限或REQUEST_COMPOMENT_RUN_IN_BACKBACK权限。只要有可能,就使用REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND.

  • 用户关闭您的应用程序的电池优化。通过将用户发送到系统设置中的应用程序信息页面,您可以帮助用户找到此选项。为此,请调用包含ACTION_IGNORE_BACKET_OPTIMIZATION_SETTINGS意图操作的意图。


可能的解决方案

解决方案%1

这将在Play Store中工作一段时间,直到Google强制升级到API级别31。

目前,从2021年11月开始,所有应用程序必须针对API级别30及以上。因此,如果您的应用程序使用API级别31,将compileSdkVersion&;targetSdkVersion降级到API级别30应该可以解决问题(至少在一段时间内)。

解决方案2

对于时间敏感的工作

如果您使用前台服务执行时间敏感的工作,请在准确的警报内启动前台服务。有关这方面的更多信息,请参阅此处的文档->;Set an exact alarm。

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

这是我的应用程序最终使用的解决方案。使用WorkManager安排并启动后台工作。有关这方面的更多信息,请参阅此处的文档->;Schedule expedited work。

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

WorkManager示例的Github回购->;WorkManager Samples

我添加此答案是因为搜索此异常不会调出任何资源来了解为什么该服务在Android 12上的行为不同。所有这些都在Google的文档中提供,并且始终记得检查文档中的行为更改。

与此更改相关的所有内容都可以在此处找到->;Android 12 Behavior Changes,特别是在Foreground Service launch restrictions中。

相关文章