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

FCM多设备令牌管理方法及新手遇到的异常问题问询

嘿,刚搞定FCM又碰到多设备令牌管理的问题,确实挺挠头的!结合我在Stack Overflow上处理过的大量类似案例,给你整理一套实用的方案,不管你后续的用户A/B场景是多设备登录、账号共享还是其他情况,这套方案都能hold住:

处理FCM多设备令牌管理的核心方案

一、先把存储逻辑捋顺——别搞一对一绑定

很多新手容易踩的坑就是把用户和令牌做一对一关联,这根本没法适配多设备场景。你得在MongoDB里单独建一个类似user_fcm_tokens的集合,每条记录对应一个设备的令牌,字段至少要包含:

  • userId:关联用户的唯一ID
  • token:FCM生成的设备令牌
  • deviceInfo:设备型号、系统版本、设备类型(手机/平板/Web),方便后续排查和定向推送
  • createdAt:令牌创建时间
  • lastActiveAt:最后一次上报活跃的时间
  • isValid:标记令牌是否有效(默认true)

而且每次客户端上报令牌时,先查库中有没有相同userId+token的记录,有就更新lastActiveAt和isValid,没有再插入,避免冗余存储。

二、常见场景的具体处理方案

1. 用户在多设备登录(比如用户A同时用手机和平板登同一个账号)

  • 客户端每次启动时,都要检查本地存储的令牌:如果没有就生成新令牌上报后端;如果有,也定期(比如每周)上报一次,更新lastActiveAt,让后端知道这个设备还在活跃。
  • 推送通知时,根据userId查询该用户下所有isValid=true的令牌,调用FCM的批量推送接口,把通知推送到所有活跃设备。

2. 令牌失效/过期的情况

FCM推送失败时会返回明确的错误码,比如InvalidRegistration或NotRegistered,这说明令牌已经失效了,你要:

  • 捕获这些错误,把对应的令牌标记为isValid=false(建议标记而非直接删除,方便后续排查问题)
  • 加个定时任务,定期清理超过30天未活跃且失效的令牌,减少无效推送的请求量

3. 用户注销/退出登录

  • 客户端退出登录时,主动调用后端接口,把当前设备的令牌标记为isValid=false
  • 如果客户端没主动通知(比如卸载APP),后端可以通过lastActiveAt判断,超过15天未活跃的令牌,标记为待清理状态

三、代码示例(MongoDB + Node.js)

存储/更新令牌的接口逻辑

async function saveOrRefreshToken(userId, token, deviceInfo) {
  // 先查有没有已存在的相同令牌
  const existingToken = await db.collection('user_fcm_tokens').findOne({ userId, token });
  
  if (existingToken) {
    // 更新活跃时间和有效性
    return db.collection('user_fcm_tokens').updateOne(
      { _id: existingToken._id },
      { $set: { lastActiveAt: new Date(), isValid: true } }
    );
  } else {
    // 插入新的令牌记录
    return db.collection('user_fcm_tokens').insertOne({
      userId,
      token,
      deviceInfo,
      createdAt: new Date(),
      lastActiveAt: new Date(),
      isValid: true
    });
  }
}

给用户批量推送通知的逻辑

async function sendNotificationToUser(userId, notificationContent) {
  // 获取该用户下所有有效令牌
  const validTokens = await db.collection('user_fcm_tokens')
    .find({ userId, isValid: true })
    .project({ token: 1 })
    .toArray();
  
  if (validTokens.length === 0) return;
  
  // 调用FCM批量推送API
  const pushResponse = await fcm.messaging().sendEachForMulticast({
    tokens: validTokens.map(item => item.token),
    notification: notificationContent
  });
  
  // 处理推送失败的失效令牌
  const invalidTokenList = pushResponse.responses
    .filter(res => !res.success && (res.error.code === 'InvalidRegistration' || res.error.code === 'NotRegistered'))
    .map((res, index) => validTokens[index].token);
  
  if (invalidTokenList.length > 0) {
    await db.collection('user_fcm_tokens').updateMany(
      { userId, token: { $in: invalidTokenList } },
      { $set: { isValid: false } }
    );
  }
}

四、额外小建议

  • 客户端一定要监听令牌刷新:FCM在令牌更新时会触发回调(比如Android的onNewToken、iOS的didReceiveRegistrationToken),一旦拿到新令牌,立刻上报后端替换旧的,避免推送失败。
  • 利用deviceInfo做定向推送:比如用户只想在手机上收通知,就可以筛选出手机类型的令牌推送,提升用户体验。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 07:49:15