Firebase通知异常:前台无弹窗后台无角标(Kotlin实现)
Firebase Messaging通知触发弹窗与角标问题解决
问题描述
- 应用前台运行时:通知显示带浅色图标角标,但点击后无法触发对应的Sheet或Popover;
- 应用后台关闭时:通知栏仅显示应用主图标无角标,但打开应用时能正常触发Sheet和Popover。
已尝试将Firebase通知优先级设为HIGH,Sheet和Popover封装在HSMApp中与MainActivity关联,但前台点击通知仍无法触发弹窗。
相关代码
MainActivity
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Initialize Firebase FirebaseApp.initializeApp(this) if (!checkPermissions()) { requestPermissions() } val notificationType: String? = intent.getStringExtra("notification_type") Log.d("MainActivity", "Received notification_type: $notificationType") setContent { // Pass the notification extra to HSMApp. HSMApp(notificationType = notificationType) } } override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) setIntent(intent) // Ensure the new intent is used val notificationType: String? = intent.getStringExtra("notification_type") Log.d("MainActivity", "New Intent notification_type: $notificationType") setContent { HSMApp(notificationType = notificationType) } }
MyFirebaseMessagingService
class MyFirebaseMessagingService : FirebaseMessagingService() { override fun onMessageReceived(remoteMessage: RemoteMessage) { super.onMessageReceived(remoteMessage) // Check if the message contains notification payload remoteMessage.notification?.let { showNotification(it.title, it.body, remoteMessage.data["type"]) } // Check if the message contains data payload if (remoteMessage.data.isNotEmpty()) { val title = remoteMessage.data["title"] val message = remoteMessage.data["message"] val type = remoteMessage.data["type"] // Expected: "devotional", "quiz", "word_for_the_day" if (!type.isNullOrEmpty()) { handleFirebaseEvent(type) showNotification(title, message, type) // Ensure notification is displayed for data messages } else { showNotification(title, message, null) } } } private fun handleFirebaseEvent(type: String) { // Create an explicit intent using our constant, then broadcast it. val intent = Intent(NOTIFICATION_TRIGGER_ACTION).apply { putExtra("type", type) } sendBroadcast(intent) } private fun showNotification(title: String?, message: String?, type: String?) { val channelId = "default_channel_id" val channelName = "Default Channel" val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager // Create a notification channel with high importance for heads-up notifications if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel( channelId, channelName, NotificationManager.IMPORTANCE_HIGH ).apply { description = "Default channel for app notifications" enableLights(true) enableVibration(true) } notificationManager.createNotificationChannel(channel) } // Make sure the Intent correctly passes "notification_type" val intent = Intent(this, MainActivity::class.java).apply { addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP) putExtra("notification_type", type) } val pendingIntent = PendingIntent.getActivity( this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) val notificationBuilder = NotificationCompat.Builder(this, channelId) .setContentTitle(title) .setContentText(message) .setSmallIcon(R.drawable.logo_image) // Ensure this icon is white on transparent background .setColor(Color.parseColor("#660d77")) .setContentIntent(pendingIntent) .setAutoCancel(true) // For pre-Oreo devices, set high priority. .setPriority(NotificationCompat.PRIORITY_HIGH) // Use defaults for sound, vibration, etc. .setDefaults(NotificationCompat.DEFAULT_ALL) notificationManager.notify(0, notificationBuilder.build()) } override fun onNewToken(token: String) { Log.d("MyAppFCM", "New token: $token") sendTokenToServer(token) } private fun sendTokenToServer(token: String) { Log.d("FCM", "Firebase token: $token") // TODO: Implement API call to send the token to your backend } }
HSMApp
HSMAppTheme { MainScreen( onDismiss = { isDevotionalSheetVisible = false isQuizSheetVisible = false isWordPopupVisible = false isMailSheetVisible = false }, showDevotionalSheet = { isDevotionalSheetVisible = true }, showQuizSheet = { isQuizSheetVisible = true }, showWordPopup = { isWordPopupVisible = true }, showMailSheet = { isMailSheetVisible = true } // Add this ) if (isDevotionalSheetVisible) { DevotionalSheet(onDismiss = { isDevotionalSheetVisible = false }) } if (isQuizSheetVisible) { QuizSheet(onDismiss = { isQuizSheetVisible = false }) } if (isWordPopupVisible) { WordForTheDayPopup(onDismiss = { isWordPopupVisible = false }) } }
解决方案
1. 前台点击通知无法触发弹窗
核心原因
HSMApp未根据传入的notificationType自动触发弹窗状态,且PendingIntent复用相同请求码导致Intent数据未更新。
修复步骤
- 给HSMApp添加自动触发逻辑:使用
LaunchedEffect监听notificationType变化,自动切换弹窗状态
@Composable fun HSMApp(notificationType: String?) { var isDevotionalSheetVisible by remember { mutableStateOf(false) } var isQuizSheetVisible by remember { mutableStateOf(false) } var isWordPopupVisible by remember { mutableStateOf(false) } var isMailSheetVisible by remember { mutableStateOf(false) } // 监听notificationType,自动触发对应弹窗 LaunchedEffect(notificationType) { notificationType?.let { type -> when(type) { "devotional" -> isDevotionalSheetVisible = true "quiz" -> isQuizSheetVisible = true "word_for_the_day" -> isWordPopupVisible = true } } } HSMAppTheme { MainScreen( onDismiss = { isDevotionalSheetVisible = false isQuizSheetVisible = false isWordPopupVisible = false isMailSheetVisible = false }, showDevotionalSheet = { isDevotionalSheetVisible = true }, showQuizSheet = { isQuizSheetVisible = true }, showWordPopup = { isWordPopupVisible = true }, showMailSheet = { isMailSheetVisible = true } ) if (isDevotionalSheetVisible) { DevotionalSheet(onDismiss = { isDevotionalSheetVisible = false }) } if (isQuizSheetVisible) { QuizSheet(onDismiss = { isQuizSheetVisible = false }) } if (isWordPopupVisible) { WordForTheDayPopup(onDismiss = { isWordPopupVisible = false }) } } }
- 修改PendingIntent请求码:避免不同类型通知复用同一请求码导致数据覆盖
// 在showNotification方法中 val requestCode = type?.hashCode() ?: 0 // 用type的哈希值作为请求码 val pendingIntent = PendingIntent.getActivity( this, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) // 同时修改通知id,避免通知被覆盖 notificationManager.notify(requestCode, notificationBuilder.build())
2. 通知角标显示异常
核心原因
- 前台通知图标不符合Android规范,导致系统渲染异常;
- Firebase默认不处理厂商角标,后台关闭时需手动调用厂商API更新角标。
修复步骤
- 确保通知图标符合规范:
R.drawable.logo_image必须是白色矢量图+透明背景,否则会被系统自动渲染为浅色; - 手动实现厂商角标更新(以华为、小米为例):
// 在showNotification方法末尾添加 // 华为设备角标更新 try { val bundle = Bundle() bundle.putString("package", packageName) bundle.putString("class", MainActivity::class.java.name) bundle.putInt("badgenumber", 1) // 角标数量,可根据需求调整 contentResolver.call( Uri.parse("content://com.huawei.android.launcher.settings/badge/"), "change_badge", null, bundle ) } catch (e: Exception) { e.printStackTrace() } // 小米设备角标更新 try { val intent = Intent("android.intent.action.APPLICATION_MESSAGE_UPDATE") intent.putExtra("android.intent.extra.update_application_component_name", "$packageName/${MainActivity::class.java.name}") intent.putExtra("android.intent.extra.update_application_message_text", "1") sendBroadcast(intent) } catch (e: Exception) { e.printStackTrace() }
内容的提问来源于stack exchange,提问作者Phela
相关产品推荐
相关产品推荐

