You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何实现同一设备上后台关闭的两个Android应用推送通知互斥显示?

这个场景我之前帮朋友处理过,核心就是让两个App能共享「是否已经显示过通知」的状态,结合FirebaseMessagingService的唤醒机制,给你几个实用的方案:

方案1:跨App共享SharedPreferences(最适合同签名App)

如果你的两个App是同一家开发的,可以给它们配置相同的sharedUserId和签名(必须同签名才能实现跨App共享),步骤如下:

  1. 在两个App的AndroidManifest.xml中添加相同的sharedUserId:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.your.app.one"
    android:sharedUserId="com.your.shared.app.id">
  1. 在FirebaseMessagingService的onMessageReceived方法里,先读取共享的状态标记:
override fun onMessageReceived(remoteMessage: RemoteMessage) {
    super.onMessageReceived(remoteMessage)
    // 获取跨App共享的SharedPreferences实例
    val sharedPref = getSharedPreferences("notification_status", Context.MODE_MULTI_PROCESS or Context.MODE_WORLD_READABLE)
    val hasNotificationShown = sharedPref.getBoolean("has_notification_shown", false)

    if (!hasNotificationShown) {
        // 显示通知逻辑
        showNotification(remoteMessage)
        // 更新标记位为已显示
        sharedPref.edit().putBoolean("has_notification_shown", true).apply()
    } else {
        // 已显示过,不做任何操作
    }
}
  1. 别忘了重置标记位:当用户清除通知时,要把标记改回false,否则下次推送两个App都不会显示。可以写个广播接收器处理通知清除事件:
class NotificationClearReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        context?.let {
            val sharedPref = it.getSharedPreferences("notification_status", Context.MODE_MULTI_PROCESS or Context.MODE_WORLD_READABLE)
            sharedPref.edit().putBoolean("has_notification_shown", false).apply()
        }
    }
}

然后在构建通知时绑定这个接收器:

private fun showNotification(message: RemoteMessage) {
    val deleteIntent = Intent(this, NotificationClearReceiver::class.java)
    val deletePendingIntent = PendingIntent.getBroadcast(
        this,
        0,
        deleteIntent,
        PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
    )

    val notificationBuilder = NotificationCompat.Builder(this, "CHANNEL_ID")
        // 其他通知配置...
        .setDeleteIntent(deletePendingIntent)

    NotificationManagerCompat.from(this).notify(1, notificationBuilder.build())
}
方案2:外部存储文件标记(无需同签名)

如果两个App不能同签名,可以用外部存储的文件来做状态标记,步骤如下:

  1. onMessageReceived里检查标记文件:
override fun onMessageReceived(remoteMessage: RemoteMessage) {
    super.onMessageReceived(remoteMessage)
    val statusFile = File(getExternalFilesDir(null), "notification_shown_flag.txt")
    val hasShown = statusFile.exists() && statusFile.readText().trim() == "true"

    if (!hasShown) {
        showNotification(remoteMessage)
        // 创建标记文件并写入状态
        statusFile.writeText("true")
    }
}
  1. 同样在通知清除时删除文件或重置内容:
// 在NotificationClearReceiver的onReceive方法里
statusFile.delete()
// 或者写入"false"

注意:Android 10及以上需要注意Scoped Storage权限,但因为是App自己的外部文件目录,权限申请相对简单。

方案3:Firebase实时数据库(跨设备也适用)

如果需要更可靠的状态同步,甚至支持跨设备场景,可以用Firebase Realtime Database:

  1. 先获取设备唯一ID(比如Android ID):
val deviceId = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)
  1. onMessageReceived里查询数据库中的状态:
override fun onMessageReceived(remoteMessage: RemoteMessage) {
    super.onMessageReceived(remoteMessage)
    val db = FirebaseDatabase.getInstance().reference
    val deviceId = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)

    db.child("device_notification_status").child(deviceId).get().addOnSuccessListener { snapshot ->
        val hasShown = snapshot.getValue(Boolean::class.java) ?: false
        if (!hasShown) {
            showNotification(remoteMessage)
            db.child("device_notification_status").child(deviceId).setValue(true)
        }
    }
}
  1. 通知清除时更新数据库状态:
db.child("device_notification_status").child(deviceId).setValue(false)

这个方案不需要App同签名,即使App卸载重装也能读取状态(只要设备ID不变),但依赖网络连接。

重要提醒
  • 后台唤醒兼容性:FirebaseMessagingService在App后台被杀死时,系统会自动唤醒进程处理消息,所以上述逻辑在后台场景下完全有效。
  • 标记重置的必要性:一定要处理通知清除的情况,否则标记一直为true,后续推送都不会显示。
  • 权限与签名:根据你的App情况选择方案,同签名选方案1,不同签名选方案2或3。

内容的提问来源于stack exchange,提问作者Zeon

火山引擎 最新活动