android 第十二章:异常:由于mAllowStartForeground为假,不允许startForegroundService()

ao218c7q  于 2022-12-09  发布在  Android
关注(0)|答案(6)|浏览(1528)

我注意到一个例外(Firebase Crashlytics)的像素5和像素4a(都在Android 12),没有其他设备,只发生了两次,一次为每个设备。
这意味着什么?Android 11和12使用前台服务的规则是一样的,但Android 11没有问题,这是泛指el的bug吗?
来自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)

brgchamk

brgchamk1#

面向Android 12(API级别31)或更高版本的应用在后台运行时无法启动前台服务,除非是少数特殊情况。如果应用在后台运行时尝试启动前台服务,而前台服务不满足其中一种特殊情况,系统将抛出ForegroundServiceStartNotAllowedException。

免除后台启动限制

  • 在以下情况下,即使应用正在后台运行,您的应用也可以启动前台服务:*
  • 您的应用从用户可见状态(如Activity)转换。
  • 您的应用可以从后台启动Activity,但应用在现有任务的后台堆栈中具有Activity的情况除外。
  • 您的应用使用Firebase Cloud Messaging接收高优先级消息。
  • 用户对与您的应用相关的UI元素执行操作。例如,他们可能与气泡、通知、小部件或Activity进行交互。
  • 您的应用会调用精确的警报来完成用户请求的操作。
  • 您的应用程序是设备的当前输入法。
  • 您的应用会收到与地理围栏或活动识别转换相关的事件。
  • 在设备重新启动并在广播接收器中接收到ACTION_BOOT_COMPLETED、ACTION_LOCKED_BOOT_COMPLETED或ACTION_MY_PACKAGE_REPLACED意图动作之后。

有关更多信息,请查看link1 link2

zc0qhyus

zc0qhyus2#

以前我们使用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,我们将无法调用服务来执行后台任务。要了解有关此限制的更多信息,请参阅Android 12行为更改。
所以从现在开始(即)从targetSdk 31 / Android 12+开始,Service只能在应用程序处于前台时调用,当应用程序关闭或应用程序转到后台时,使用startForegroundService调用Service将导致ForegroundServiceStartNotAllowedException,因此要在Android 12及以上版本中执行后台任务,我们需要使用Worker而不是Service。要了解有关Worker的更多信息,请参阅工作请求。
因此,对于面向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 );
}

BackupService(现有)的示例代码。

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 );
    }
}

BackupWorker的示例代码(新增)。

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'

我已经测试了上面的代码与Android 5,Android 8,Android 11和Android 12一起工作。在我的情况下,工作如预期。
希望这个解决方案能帮助那些将他们的应用程序定位为SDK 31 / Android 12+的人。

fdbelqdn

fdbelqdn3#

在我的例子中,我们使用了一个进行SIP通信(VoIP)的服务,并且我们启动了一个前台服务来进行时间敏感的操作(比如注册)或者运行SIP调用。
为了处理这个用例,Android允许你声明你的服务的“foregroundServiceType“,并且允许一些类型从后台创建前台服务。另一个值得注意的用例是媒体播放。

5ktev3wc

5ktev3wc4#

要在Android 12或更高版本中修复此问题,请更新应用的逻辑:
如果您发现应用在后台运行时启动了前台服务,请更新应用的逻辑以使用WorkManager。要查看如何更新应用的示例,请查看GitHub上的WorkManagerSample

i2loujxw

i2loujxw5#

我们能把它作为基本解决方案吗?

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 );
            }
4szc88ey

4szc88ey6#

我发现的最好的解决方案是从后台启动服务,或者在设备通过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) {
    notificationBuilder.foregroundServiceBehavior = FOREGROUND_SERVICE_IMMEDIATE
}

notificationBuilder.setPriority(NotificationCompat.PRIORITY_MAX)

相关问题