Firebase Messaging Android SDK:Token刷新后重新订阅Topic问题咨询
Thread.sleep() 的解决方案 我之前在做FCM Topic相关的SDK集成时,碰到过和你几乎一模一样的问题!先确认你关于Firebase SDK的结论:没错,Firebase确实不会在Instance ID Token刷新后自动维护Topic订阅,官方文档里也明确提到,当Token更新后,之前的订阅关联会失效,必须手动重新订阅。
至于你说的必须加Thread.sleep()才能让订阅成功,本质原因是Token刷新后,Firebase后端需要一点时间完成新Token的同步,如果你在同步完成前就发起订阅请求,后端会因为无法识别这个新Token而返回失败。但Thread.sleep()绝对不是靠谱的解决方案——不同网络环境下同步时间差异很大,sleep时间短了还是会失败,长了又会阻塞线程影响用户体验。
分享我当时解决这个问题的几个关键做法,亲测有效:
确保获取到稳定的新Token再执行订阅
不要在onNewToken回调触发后立刻发起订阅,最好通过FirebaseMessaging.getInstance().getToken()的回调来确认Token已经完全更新完成,再执行后续的订阅操作。毕竟onNewToken只是通知你Token发生了变化,此时新Token可能还没完全同步到后端。给订阅请求添加指数退避的重试机制
即使Token同步完成,网络波动或者后端限流也可能导致订阅失败。给每个订阅请求加3-5次重试,每次重试的间隔时间按指数增长(比如第一次等1秒,第二次2秒,第三次4秒),这种方式比固定Thread.sleep()灵活得多,也更可靠。避免批量订阅时并发请求
如果需要订阅多个Topic,不要同时发起所有请求,而是串行处理(比如用异步任务依次执行)。短时间内给Firebase后端发送大量同一Token的订阅请求,很容易触发限流,导致请求失败。
举个简单的Kotlin代码示例(Java思路类似):
// 在Token刷新完成后执行订阅 FirebaseMessaging.getInstance().token.addOnCompleteListener { task -> if (task.isSuccessful) { val newToken = task.result // 从本地缓存取出用户之前订阅的Topics列表 val subscribedTopics = getSavedSubscribedTopics() subscribedTopics.forEachIndexed { index, topic -> // 指数退避延迟:1s, 2s, 4s... val delayMillis = 1000L * (1 shl index) Handler(Looper.getMainLooper()).postDelayed({ subscribeWithRetry(topic, retryCount = 3) }, delayMillis) } } } private fun subscribeWithRetry(topic: String, retryCount: Int) { FirebaseMessaging.getInstance().subscribeToTopic(topic) .addOnFailureListener { error -> if (retryCount > 0) { // 重试次数减一,再次发起请求 subscribeWithRetry(topic, retryCount - 1) } else { // 处理最终失败的情况,比如记录日志、后续再尝试 Log.e("FCM", "订阅Topic $topic 最终失败: ${error.message}") } } }
我当时就是把Thread.sleep()换成了这套方案,之后再也没出现过订阅失败的问题,稳定性提升了很多。
内容的提问来源于stack exchange,提问作者NoyG_Optimove

