所以,我一直在尝试使用新的Android模拟器(即Android 13或Android Tiramisu,API 33),但在一个应用中,我需要一个前台服务。
该应用的目标SDK当前为33。
由于通知要求,我已将android.permission.POST_NOTIFICATIONS
添加到清单文件中。因为它也是运行时权限,所以我会在打开应用后请求权限。
如果用户拒绝了权限,但在使用startForegroundService
启动前台服务之后尝试执行涉及前台服务的任务,则在从我的服务调用startForeground
时,我会遇到崩溃:
android.app.RemoteServiceException$CannotPostForegroundServiceNotificationException: Bad notification for startForeground
at android.app.ActivityThread.throwRemoteServiceException(ActivityThread.java:1983)
at android.app.ActivityThread.-$$Nest$mthrowRemoteServiceException(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2242)
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:7898)
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:936)
请注意异常名称:CannotPostForegroundServiceNotificationException。所以显然系统有效地防止我发布前台服务通知。这是在使用有效通知(至少到API 32为止,而我没有看到在构建通知本身方面在API 32和API 33之间有任何变化,除了setOngoing(true)
,这是我已经在做的)之后发生的。
因此,我检查了是否可以发布通知,使用NotificationManager.areNotificationsEnabled()
。如果用户拒绝权限,则返回false,正如预期的那样。现在代码看起来像这样:
if (mNotificationManager.areNotificationsEnabled())
startForeground(123, mNotificationBuilder.build())
而且,正如预期的那样,startForeground
不会被调用。但是,需要执行的任务可能很长(大约2分钟),并且必须在后台执行,这不能通过作业或通过WorkManager
执行,并且如果没有调用startForeground
,则应用程序在约20秒后会引发以下异常:
android.app.RemoteServiceException$ForegroundServiceDidNotStartInTimeException: Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{ecbbdfb u0 com.example.android/.service.FgService}
at android.app.ActivityThread.generateForegroundServiceDidNotStartInTimeException(ActivityThread.java:2006)
at android.app.ActivityThread.throwRemoteServiceException(ActivityThread.java:1977)
at android.app.ActivityThread.-$$Nest$mthrowRemoteServiceException(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2242)
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:7898)
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:936)
Caused by: android.app.StackTrace: Last startServiceCommon() call for this service was made here
at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1915)
at android.app.ContextImpl.startForegroundService(ContextImpl.java:1870)
at android.content.ContextWrapper.startForegroundService(ContextWrapper.java:822)
at com.example.android.MainActivity.startTaskWithFgService(MainActivity.kt:30)
at com.example.kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:749)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)
需要注意的是,如果用户接受了通知权限,通知将正常显示,因此这似乎是一个权限问题,并且没有观察到崩溃。
编辑:创建的通知渠道应该发布静默通知。 因此,这就是创建通知渠道的方式(也包括尝试发布通知时使用的方式):
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
withNotificationManager {
if (notificationChannels?.map { it.id }?.contains(SILENT_NOTIF_CHANNEL_ID) == true)
return@withNotificationManager
val channel = NotificationChannel(
SILENT_NOTIF_CHANNEL_ID,
getString(R.string.silent_notif_channel_name),
NotificationManager.IMPORTANCE_MIN).apply {
enableLights(false)
setShowBadge(false)
setSound(null, null)
description = getString(R.string.silent_notif_channel_desc)
vibrationPattern = null
lockscreenVisibility = Notification.VISIBILITY_SECRET
}
createNotificationChannel(channel)
}
}
据我的理解,发生了两件事情:
- 我无法使用通知启动前台服务,因为权限被拒绝了,
- 我也无法在没有通知的情况下启动前台服务,因为通知是必需的。
这两个条件有效地移除了前台服务功能。 我认为这看起来像是一个疏忽。
引用自Android 13行为变更通知权限(链接):
"应用程序无需请求POST_NOTIFICATIONS权限即可启动前台服务。 但是,与之前的Android版本一样,应用程序在启动前台服务时必须包含通知。"
所以,我的问题是:
如果用户拒绝了权限,我该如何在后台执行长时间任务而不需要前台服务?
感谢阅读问题,并感激任何帮助、答案或讨论。