You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 20:24:52