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

求助:如何实现Android全设备Firebase推送通知应用图标角标(默认开启+低版本兼容)

解决方案:Firebase推送实现全版本Android应用图标角标

我来帮你搞定这两个角标适配的问题,结合Firebase推送的场景,给你整理了经过真机验证的可行方案:

一、Android 8+:引导默认启用角标(减少手动操作)

首先得明确:Android 8+系统没有原生API能强制默认开启应用图标角标,因为这属于用户的通知权限范畴。但我们可以通过以下步骤,让角标功能自动生效,避免用户手动找设置:

  1. 创建Notification Channel时强制开启角标支持
    在初始化Firebase推送的通知渠道时,一定要加上setShowBadge(true)——这是系统允许该渠道通知显示角标的基础配置,没加的话就算用户开了权限也白搭:

    val channelId = "your_push_channel"
    val channelName = "应用推送通知"
    val channel = NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_DEFAULT).apply {
        description = "接收应用的实时推送消息"
        setShowBadge(true) // 核心配置:开启渠道的角标支持
    }
    val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    notificationManager.createNotificationChannel(channel)
    
  2. 自动检测并引导用户开启角标权限
    不同厂商的角标权限入口不一样,我们可以在App首次启动或收到第一条推送前,检测权限状态,未开启就直接跳转到对应厂商的设置页面:

    fun checkAndGuideBadgePermission(context: Context) {
        // 先检查基础通知权限(角标依赖通知权限)
        if (!NotificationManagerCompat.from(context).areNotificationsEnabled()) {
            val intent = Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply {
                putExtra(Settings.EXTRA_APP_PACKAGE, context.packageName)
            }
            context.startActivity(intent)
            return
        }
    
        // 针对主流厂商做引导适配
        val manufacturer = Build.MANUFACTURER.lowercase()
        when {
            manufacturer.contains("xiaomi") -> {
                val intent = Intent("android.settings.APP_NOTIFICATION_SETTINGS").apply {
                    putExtra("app_package", context.packageName)
                    putExtra("app_uid", context.applicationInfo.uid)
                }
                context.startActivity(intent)
            }
            manufacturer.contains("huawei") -> {
                val intent = Intent("com.huawei.android.launcher.action.CHANGE_BADGE").apply {
                    putExtra("package", context.packageName)
                    putExtra("class", context.packageManager.getLaunchIntentForPackage(context.packageName)?.component?.className)
                }
                // 找不到专属入口就跳应用详情页
                if (context.packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) == null) {
                    val fallbackIntent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
                        data = Uri.fromParts("package", context.packageName, null)
                    }
                    context.startActivity(fallbackIntent)
                } else {
                    context.startActivity(intent)
                }
            }
            // 三星、OPPO、vivo等厂商可参照上述逻辑添加适配
        }
    }
    

二、Android 8以下版本:适配各大厂商自定义角标API

Android 8及以下原生不支持角标,必须适配主流厂商的自定义API,下面是封装好的通用工具类,直接用就行:

角标工具类:BadgeUtils

object BadgeUtils {
    fun updateBadgeCount(context: Context, count: Int) {
        val manufacturer = Build.MANUFACTURER.lowercase()
        try {
            when {
                manufacturer.contains("xiaomi") -> setXiaomiBadge(context, count)
                manufacturer.contains("huawei") -> setHuaweiBadge(context, count)
                manufacturer.contains("samsung") -> setSamsungBadge(context, count)
                manufacturer.contains("oppo") -> setOppoBadge(context, count)
                manufacturer.contains("vivo") -> setVivoBadge(context, count)
            }
        } catch (e: Exception) {
            // 避免厂商API变更导致崩溃
            e.printStackTrace()
        }
    }

    // 小米角标设置
    private fun setXiaomiBadge(context: Context, count: Int) {
        val intent = Intent("android.intent.action.APPLICATION_MESSAGE_UPDATE").apply {
            val launcherComponent = context.packageManager.getLaunchIntentForPackage(context.packageName)?.component
            putExtra("android.intent.extra.update_application_component_name", launcherComponent?.flattenToString())
            putExtra("android.intent.extra.update_application_message_count", count)
        }
        context.sendBroadcast(intent)
    }

    // 华为角标设置
    private fun setHuaweiBadge(context: Context, count: Int) {
        val bundle = Bundle().apply {
            putString("package", context.packageName)
            putString("class", context.packageManager.getLaunchIntentForPackage(context.packageName)?.component?.className)
            putInt("badgenumber", count)
        }
        context.contentResolver.call(Uri.parse("content://com.huawei.android.launcher.settings/badge/"), "change_badge", null, bundle)
    }

    // 三星角标设置
    private fun setSamsungBadge(context: Context, count: Int) {
        val intent = Intent("android.intent.action.BADGE_COUNT_UPDATE").apply {
            putExtra("badge_count", count)
            putExtra("badge_count_package_name", context.packageName)
            putExtra("badge_count_class_name", context.packageManager.getLaunchIntentForPackage(context.packageName)?.component?.className)
        }
        context.sendBroadcast(intent)
    }

    // OPPO角标设置
    private fun setOppoBadge(context: Context, count: Int) {
        try {
            val intent = Intent("com.oppo.unsettledevent").apply {
                putExtra("pakeageName", context.packageName)
                putExtra("number", count)
            }
            if (context.packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null) {
                context.sendBroadcast(intent)
            } else {
                val contentValues = ContentValues().apply {
                    put("package", context.packageName)
                    put("class", context.packageManager.getLaunchIntentForPackage(context.packageName)?.component?.className)
                    put("badgenumber", count)
                }
                context.contentResolver.insert(Uri.parse("content://com.android.badge/badge"), contentValues)
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    // VIVO角标设置
    private fun setVivoBadge(context: Context, count: Int) {
        val intent = Intent("launcher.action.CHANGE_APPLICATION_NOTIFICATION_NUM").apply {
            putExtra("packageName", context.packageName)
            putExtra("className", context.packageManager.getLaunchIntentForPackage(context.packageName)?.component?.className)
            putExtra("notificationNum", count)
        }
        context.sendBroadcast(intent)
    }
}

结合Firebase推送使用

在你的FirebaseMessagingService中,收到推送后调用工具类更新角标(注意自己维护未读计数,比如用SharedPreferences存储):

class MyFirebaseMessagingService : FirebaseMessagingService() {
    override fun onMessageReceived(remoteMessage: RemoteMessage) {
        super.onMessageReceived(remoteMessage)
        // 处理推送内容、创建通知...

        // 更新角标计数(示例:从SP读取当前未读数+1)
        val sp = getSharedPreferences("app_config", MODE_PRIVATE)
        val currentUnread = sp.getInt("unread_count", 0)
        val newUnread = currentUnread + 1
        sp.edit().putInt("unread_count", newUnread).apply()
        
        BadgeUtils.updateBadgeCount(this, newUnread)
    }
}

额外注意事项

  • 部分厂商需要声明权限,比如华为要在AndroidManifest.xml中添加:
    <uses-permission android:name="com.huawei.android.launcher.permission.CHANGE_BADGE"/>
    
  • 角标计数需要自己维护,用户查看消息时记得清零,调用BadgeUtils.updateBadgeCount(context, 0)即可。
  • 一定要在真机上测试,模拟器大多不支持厂商自定义角标。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 03:57:33