添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

致命的安卓12:异常:由于mAllowStartForeground为假,startForegroundService()不允许。

42 人关注

我注意到Pixel 5和Pixel 4a(都在安卓12系统上)有一个例外(Firebase Crashlytics),其他设备没有,只发生了两次,每个设备一次。

这是什么意思?安卓11和12有相同的前台服务工作规则,但安卓11没有问题。这是Pixel的一个错误吗?

来自Firebase Crashlytics。

Fatal Exception: android.app.ForegroundServiceStartNotAllowedException
startForegroundService() not allowed due to mAllowStartForeground false: service com.*.*/.service.RecorderService
android.app.ForegroundServiceStartNotAllowedException$1.createFromParcel (ForegroundServiceStartNotAllowedException.java:54)
androidx.core.content.ContextCompat.startForegroundService (ContextCompat.java:6)
MyAppPackageHidden.service.RecorderService$Companion.startService (RecorderService.java:2)
MyAppPackageHidden.ui.rec.RecActivity$getConnectionRecorderService$1.onServiceConnected (RecActivity.java:4)
android.app.LoadedApk$ServiceDispatcher.doConnected (LoadedApk.java:2077)
com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1003)
Fatal Exception: android.app.ForegroundServiceStartNotAllowedException: startForegroundService() not allowed due to mAllowStartForeground false: service MyAppPackageHidden/.service.RecorderService
       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 androidx.core.content.ContextCompat$Api26Impl.startForegroundService(ContextCompat.java)
       at androidx.core.content.ContextCompat.startForegroundService(ContextCompat.java:6)
       at MyAppPackageHidden.service.RecorderService$Companion.startService(RecorderService.java:2)
       at MyAppPackageHidden.ui.rec.RecActivity$getConnectionRecorderService$1.onServiceConnected(RecActivity.java:4)
       at android.app.LoadedApk$ServiceDispatcher.doConnected(LoadedApk.java:2077)
       at android.app.LoadedApk$ServiceDispatcher$RunConnection.run(LoadedApk.java:2110)
       at android.os.Handler.handleCallback(Handler.java:938)
       at android.os.Handler.dispatchMessage(Handler.java:99)
       at android.os.Looper.loopOnce(Looper.java:201)
       at android.os.Looper.loop(Looper.java:288)
       at android.app.ActivityThread.main(ActivityThread.java:7838)
       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)
Caused by android.os.RemoteException: Remote stack trace:
    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)
    
4 个评论
也许在你打电话给 RecActivity 的时候, startForegroundService() 正在后台。
@CommonsWare 我想是的。活动有 bindService ,它将 ServiceConnection 对象作为Callback,在 onServiceConnected 方法中,我使服务成为前台。我想我需要使用 lifecycleScope.launchWhenStarted { /* start foreground */ }
另请参见关于 ForegroundServiceStartNotAllowedException 的相关问题和可能的解决方案。 here
@user924 你是如何解决这个问题的。我也面临音频服务的同样问题。你能分享一下解决方案吗?
android
android-service
android-12
user924
user924
发布于 2021-11-20
6 个回答
Himanshi Thakur
Himanshi Thakur
发布于 2022-12-08
已采纳
0 人赞同

针对Android 12(API级别31)或更高的应用程序不能在后台运行时启动前台服务,少数特殊情况除外。如果一个应用程序在后台运行时试图启动一个前台服务,而该前台服务不满足其中一种特殊情况,系统会抛出一个ForegroundServiceStartNotAllowedException。

背景启动限制的豁免

在以下情况下,即使你的应用在后台运行,你的应用也可以启动前台服务。

  • Your app transitions from a user-visible state, such as an activity.
  • Your app can start an activity from the background, except for the case where the app has an activity in the back stack of an existing task.
  • Your app receives a high-priority message using Firebase Cloud Messaging.
  • The user performs an action on a UI element related to your app. For example, they might interact with a bubble, notification, widget, or activity.
  • Your app invokes an exact alarm to complete an action that the user requests.
  • Your app is the device's current input method.
  • Your app receives an event that's related to geofencing or activity recognition transition.
  • After the device reboots and receives the ACTION_BOOT_COMPLETED, ACTION_LOCKED_BOOT_COMPLETED, or ACTION_MY_PACKAGE_REPLACED intent action in a broadcast receiver.
  • 更多信息请查看 link1 link2

    解释得很好,但解决方案是什么?
    我在清单中声明了 foregroundServiceType="mediaPlayback" ,而且该服务只在点击我的应用程序中的通知或播放按钮后启动,所以我希望它能工作。尽管如此,我每个月仍然有几百次崩溃,这是安卓系统中最神秘的领域之一。
    @avalancha 你曾想过这个问题吗?我有一个类似的情况。
    绝对不是。这是谷歌让我们完全被晾在一边的领域之一
    我发现,如果你的应用程序试图启动你的FG服务,但由于用户的行动,如接听电话(基本上任何会使你的应用程序BG化的东西)而被打断并进入后台,你会遇到这个错误。 因为它在服务内部执行 startForeground(notificationId, notification); ,整个过程被杀死。
    niranj1997
    niranj1997
    发布于 2022-12-08
    0 人赞同

    之前我们是用 Service 来运行后台任务,如备份数据、设置提醒通知等。而之前调用服务的代码将如下所示

    Intent serviceIntent = new Intent ( context, BackupService.class );
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        context.startForegroundService ( serviceIntent );
    } else {
        context.startService ( serviceIntent );
    

    但是,由于Android 12 - Foreground service launch restrictions,我们将无法调用服务来执行后台任务。要了解有关这一限制的更多信息,请参考安卓12的行为变化.

    所以从现在开始,(即)从targetSdk 31 / Android 12+开始,Service只能在应用程序处于前台的时候调用。当应用程序被关闭或进入后台时,使用startForegroundService调用Service将导致ForegroundServiceStartNotAllowedException。因此,要在Android 12及以上版本中执行后台任务,我们需要使用Worker而不是Service。要了解更多关于Worker的信息,请参考Work Requests.

    因此,对于以SDK 31/Android 12+为目标的应用程序,调用后台任务的代码将如下。

    Intent serviceIntent = new Intent ( context, BackupService.class );
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        OneTimeWorkRequest request = new OneTimeWorkRequest.Builder ( BackupWorker.class ).addTag ( "BACKUP_WORKER_TAG" ).build ();
        WorkManager.getInstance ( context ).enqueue ( request );
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        context.startForegroundService ( serviceIntent );
    } else {
        context.startService ( serviceIntent );
    

    替换代码11】(Existing)的示例代码。

    public class BackupService extends Service {
        private static final String TAG = "BackupService";
        @Nullable
        @Override
        public IBinder onBind ( Intent intent ) {
            return null;
        @Override
        public int onStartCommand ( Intent intent, int flags, int startId ) {
            Log.d ( TAG, "onStartCommand" );
            startForeground ( BACKUP_SERVICE_NOTIFICATION_ID, createServiceNotification () );
            //call methods to perform background task
            return super.onStartCommand ( intent, flags, startId );
    

    替换代码13】的示例代码(新添加)。

    public class BackupWorker extends Worker {
        private static final String TAG = "BackupWorker";
        public BackupWorker ( @NonNull Context context, @NonNull WorkerParameters workerParams ) {
            super ( context, workerParams );
        @NonNull
        @Override
        public Result doWork () {
            //call methods to perform background task
            return Result.success ();
    

    请确保在模块级的gradle文件中添加以下依赖项

    implementation 'androidx.work:work-runtime:2.7.1'
    implementation 'com.google.guava:guava:27.0.1-android'
    

    I have tested the above code working with Android 5, Android 8, Android 11 & Android 12. Working as expected in my case.

    希望这个解决方案能帮助那些针对SDK 31/Android 12+的应用程序的人。

    Lars
    那么,如果让应用程序始终运行前台服务,从而不仅仅是为了几个任务,对应用程序来说是很重要的呢?
    嘿,@Lars !对于Target SDK 31及以上版本,应用程序只能在少数任务中使用前台服务,如媒体播放、媒体投射、电话和其他少数任务。因此,对于其他任务,我们需要使用任何后台进程,如 Worker 。你可以参考这个页面以找到更多相关信息 :) developer.android.com/guide/components/foreground-services
    Lars
    谢谢你,我就怕这个。我想要求用户禁用电池优化是我要做的事。谢谢你的快速答复。
    我在安卓12的谷歌像素上没有任何问题,但三星是另一个故事。为什么?
    对于较低的Android版本,没有必要仍然使用BackupService。你也可以在那里使用一个Worker。
    Guss
    Guss
    发布于 2022-12-08
    0 人赞同

    在我的案例中,我们使用一个做SIP通信(VoIP)的服务,我们正在启动一个前台服务来做时间敏感的操作(如注册)或在运行SIP呼叫时。这些任务不能在 Worker 中运行。

    为了处理这个用例。 Android允许你声明你的服务的" foregroundServiceType ",有些类型允许从后台创建前台服务 .另一个值得注意的用例是媒体播放。

    我已经宣布我的像这样的 android:foregroundServiceType="mediaPlayback" ,但我仍然得到这个崩溃。我有一个与该服务相关的媒体通知。有什么想法吗?
    Guss
    在进一步研究之后,我意识到这个答案是错误的--从安卓12开始,没有 foregroundServiceType 可以设置,允许你启动一个前台服务,否则应用程序将被排除在外--安卓12允许前台服务的启发式方法很复杂,但是不受使用 foregroundServiceType 的影响。
    Guss
    我现在的实现是尝试使用 startForgroundService() 来启动前台服务,然后如果失败了--由于OP的错误--捕捉抛出的异常并使用 AlarmManager 作为回退,如这里所解释的。 stackoverflow.com/a/53759060/53538
    0 人赞同

    我们可以把这个作为基本解决方案吗?

     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                    context.startService ( Service_name );
                } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    ContextCompat.startForegroundService(
                            context,
                            Service_name
                } else {
                    context.startService ( Service_name );
        
    Shailendra Madda
    Shailendra Madda
    发布于 2022-12-08
    0 人赞同

    为了解决这个问题,在安卓12或更高版本中更新你的应用程序的逻辑。

    如果你发现你的应用程序在后台运行时启动前台服务,请更新你的应用程序的逻辑,使用 工作经理 .要查看如何更新你的应用程序的例子,请通过以下方式查看 工作经理Sample on GitHub.

    @user924 是的,但这是在Android 12及以上版本中避免这种异常的唯一方法。
    为了从后台启动一个前台服务,我们可以禁用一个应用程序的电池优化,然后它就可以毫无问题地工作。
    但这并不是一个理想的解决方案,通过禁用电池优化来实现。
    EAK TEAM
    EAK TEAM
    发布于 2022-12-08
    0 人赞同

    我发现的最佳解决方案是通过 Android O+ 上的 AlarmManager 从后台或设备重启后启动服务。

    val mgr = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
    val i = Intent(context, Service::class.java)
    val pi = PendingIntent.getForegroundService(context, 0, i, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
    val calendar: Calendar = Calendar.getInstance()
    calendar.timeInMillis = System.currentTimeMillis()
    calendar.add(Calendar.SECOND, 3)
    mgr.set(AlarmManager.RTC_WAKEUP, calendar.timeInMillis ,pi)
    

    另外,在NotificationBuilder中增加了。

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {