如何向MainActivity发送FCM消息并实现底部导航徽章显示(解决重复打开)
解决FCM消息传递与底部导航栏徽章显示的问题
嘿,我来帮你搞定这两个需求,一步步拆解:
一、如何向MainActivity发送FCM消息
FCM消息主要分两类,对应不同的传递方式:
- 通知消息:当App在后台时,系统会自动展示通知,点击后默认打开App的启动Activity。如果想指定打开MainActivity,需要在FCM的payload里配置
click_action为MainActivity的全类名(比如com.yourpackage.MainActivity),同时在Manifest里给MainActivity添加对应的intent-filter:
对应的FCM payload(JSON格式)示例:<activity android:name=".MainActivity"> <intent-filter> <action android:name="com.yourpackage.MAIN_ACTIVITY" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity>{ "notification": { "title": "新消息提醒", "body": "你有一条未读消息", "click_action": "com.yourpackage.MAIN_ACTIVITY" }, "to": "目标设备令牌" } - 数据消息:不管App在前台还是后台,都会直接触发
onMessageReceived方法,这也是你当前使用的方式,接下来重点解决这种方式下的Activity实例重复问题。
二、解决MainActivity重复实例+底部导航栏徽章显示
你当前代码用FLAG_ACTIVITY_NEW_TASK会创建新的Activity实例,我们可以通过调整Intent的Flag复用已存在的MainActivity,再通过onNewIntent接收数据并更新徽章:
1. 修改FCM Service中的代码
把Intent的Flag改成FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP,这样如果MainActivity已经在栈中,就会复用它并调用onNewIntent方法,而不是创建新实例:
override fun onMessageReceived(p0: RemoteMessage) { super.onMessageReceived(p0) val intent = Intent(this, MainActivity::class.java) // 替换原有Flag,复用已存在的MainActivity实例 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP) // 传递消息数据,兼容通知消息和数据消息的内容 intent.putExtra("badge", p0.notification?.body ?: p0.data["badge"]) // 如果App在后台,建议配合PendingIntent构建系统通知,点击后唤醒Activity val pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE) // 这里可以选择性构建系统通知(比如App后台时提示用户) // ...(通知构建代码) startActivity(intent) }
2. 在MainActivity中接收数据并显示徽章
重写onNewIntent方法获取传递的badge数据,然后给底部导航栏的对应item添加徽章:
class MainActivity : AppCompatActivity() { private lateinit var bottomNav: BottomNavigationView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) bottomNav = findViewById(R.id.bottom_navigation) // 检查启动时是否有传递的badge数据 intent?.getStringExtra("badge")?.let { updateBadge() } } override fun onNewIntent(intent: Intent?) { super.onNewIntent(intent) setIntent(intent) // 更新当前Intent,确保后续获取数据正确 intent?.getStringExtra("badge")?.let { updateBadge() } } private fun updateBadge() { // 获取底部导航栏的目标item(比如首页对应的index为0) val targetItemId = bottomNav.menu.getItem(0).itemId // 创建或获取徽章实例 val badge = bottomNav.getOrCreateBadge(targetItemId) badge.isVisible = true // 这里用计数方式更新徽章,也可以设置自定义文本 badge.number = badge.number + 1 } }
补充小提示
- 如果App处于后台状态,单纯调用
startActivity可能无法直接唤醒Activity,配合系统通知+PendingIntent的方式体验会更好。 - 要是App在前台时想实时更新徽章,用LiveData/ViewModel或者本地广播传递数据会比直接启动Activity更优雅哦。
内容的提问来源于stack exchange,提问作者Mohammad Derakhshan




