应用被杀死状态下如何通过WorkManager实现底部弹出的高优先级定时通知
问题描述
需要在应用被杀死状态下实现从屏幕底部弹出的高优先级类通知效果,也可以是任何具备高优先级属性、从底部弹出的类通知交互组件,同时要求使用WorkManager实现每日指定时间触发该效果。经过大量调研未找到相关解决方案。
已尝试方案
尝试在Worker类的doWork方法中展示Snackbar,当前实现代码如下:
class NotifyWork(context: Context, params: WorkerParameters) : Worker(context, params) { override fun doWork(): Result { // val mySnackbar = Snackbar() val id = inputData.getLong(NOTIFICATION_ID, 0).toInt() sendNotification(id) return success() } private fun sendNotification(id: Int) { val intent = Intent(applicationContext, MainActivity::class.java) intent.flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK intent.putExtra(NOTIFICATION_ID, id) val notificationManager = applicationContext.getSystemService(NOTIFICATION_SERVICE) as NotificationManager val notificationLayoutExpanded = RemoteViews(applicationContext.packageName, R.layout.layout_notification) val bitmap = applicationContext.vectorToBitmap(R.mipmap.ic_launcher) val pendingIntent = getActivity(applicationContext, 0, intent, 0) val notification = NotificationCompat.Builder(applicationContext, NOTIFICATION_CHANNEL) .setLargeIcon(bitmap) .setSmallIcon(R.drawable.ic_launcher_foreground) .setAutoCancel(true) .setPriority(PRIORITY_MAX) .setCustomContentView(notificationLayoutExpanded) .setOngoing(true) .setFullScreenIntent(pendingIntent, true) .setContentIntent(pendingIntent) notification.priority = PRIORITY_MAX if (SDK_INT >= O) { notification.setChannelId(NOTIFICATION_CHANNEL) val ringtoneManager = getDefaultUri(TYPE_NOTIFICATION) val audioAttributes = AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE) .setContentType(CONTENT_TYPE_SONIFICATION).build() val channel = NotificationChannel(NOTIFICATION_CHANNEL, NOTIFICATION_NAME, IMPORTANCE_HIGH) channel.enableLights(true) channel.lightColor = RED channel.enableVibration(true) channel.vibrationPattern = longArrayOf(100, 200, 300, 400, 500, 400, 300, 200, 400) channel.setSound(ringtoneManager, audioAttributes) notificationManager.createNotificationChannel(channel) } notificationManager.notify(id, notification.build()) } companion object { const val NOTIFICATION_ID = "appName_notification_id" const val NOTIFICATION_NAME = "appName" const val NOTIFICATION_CHANNEL = "appName_channel_01" const val NOTIFICATION_WORK = "appName_notification_work" } }
现有代码仅能在屏幕顶部展示高优先级系统通知,无法实现从底部弹出的预期效果。
可行解决方案
系统默认通知本身仅支持顶部弹出,要实现应用被杀死状态下的底部弹出类通知效果,需采用悬浮窗权限+透明Activity的方案,配合WorkManager定时触发,具体实现步骤如下:
- 步骤1:声明必要权限与组件
首先在AndroidManifest.xml中添加所需权限,同时注册用来展示底部弹窗的透明Activity:
<!-- 悬浮窗权限 --> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <!-- Android 12+ 前台服务权限 --> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <!-- 通知权限 --> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <application ...> <!-- 底部弹窗透明Activity --> <activity android:name=".BottomPopupActivity" android:theme="@style/TransparentActivityTheme" android:exported="false" android:launchMode="singleTask" /> </application>
在res/values/styles.xml中定义透明Activity主题:
<style name="TransparentActivityTheme" parent="Theme.MaterialComponents.Light.NoActionBar"> <item name="android:windowBackground">@android:color/transparent</item> <item name="android:windowIsTranslucent">true</item> <item name="android:windowAnimationStyle">@android:style/Animation.Translucent</item> <item name="android:windowContentOverlay">@null</item> <item name="android:backgroundDimEnabled">false</item> </style>
- 步骤2:实现底部弹窗透明Activity
在BottomPopupActivity中使用BottomSheetDialog或者自定义动画实现从底部弹出的交互效果,弹窗关闭后直接销毁Activity即可:
class BottomPopupActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) showBottomPopup() } private fun showBottomPopup() { val bottomSheetDialog = BottomSheetDialog(this) // 加载自定义的底部弹窗布局 val popupView = layoutInflater.inflate(R.layout.your_custom_bottom_popup, null) bottomSheetDialog.setContentView(popupView) // 配置弹窗可在应用外部显示 bottomSheetDialog.window?.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY) bottomSheetDialog.setCancelable(true) // 弹窗关闭时同步销毁Activity bottomSheetDialog.setOnDismissListener { finish() } bottomSheetDialog.show() } }
- 步骤3:修改Worker触发逻辑
原有Worker中不再发送系统通知,改为判断悬浮窗权限后启动透明Activity,无权限时可降级为原有顶部通知:
class NotifyWork(context: Context, params: WorkerParameters) : Worker(context, params) { override fun doWork(): Result { triggerBottomPopup() return Result.success() } private fun triggerBottomPopup() { // 检查悬浮窗权限 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (!Settings.canDrawOverlays(applicationContext)) { // 无权限时降级触发原有顶部通知 sendFallbackNotification() return } } // 启动底部弹窗Activity val intent = Intent(applicationContext, BottomPopupActivity::class.java) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) applicationContext.startActivity(intent) } private fun sendFallbackNotification() { // 此处复用你原有发送顶部高优先级通知的代码 // ... } companion object { const val NOTIFICATION_ID = "appName_notification_id" const val NOTIFICATION_NAME = "appName" const val NOTIFICATION_CHANNEL = "appName_channel_01" const val NOTIFICATION_WORK = "appName_notification_work" } }
- 步骤4:引导用户授权
应用首次启动时引导用户开启悬浮窗权限、通知权限,以及后台弹出界面等厂商定制权限,保证功能正常触发。
注意事项
- 国内各大定制ROM对后台启动Activity、悬浮窗权限有额外限制,需针对不同厂商做权限引导适配
- WorkManager的定时任务在低电模式、应用被强制停止场景下可能被系统延迟触发,如有强准时要求可配合AlarmManager使用
内容的提问来源于stack exchange,提问作者Bakai Ismailov
相关产品推荐
相关产品推荐

