基于Java SDK的FCM消息推送:如何正确处理并区分各类失败情况
正确区分FCM推送各类失败情况的处理方案
你现在只捕获了ExecutionException,但其实这个异常只是CompletableFuture把实际的FCM异常包装了一层——真正的错误原因藏在它的getCause()里。要精准处理不同的推送失败,得先拿到这个实际异常,再根据FCM SDK提供的异常类型来区分场景。
针对你用的com.google.firebase:firebase-admin v5.9.0,具体可以这么做:
1. 先捕获所有可能的异常
sendAsync().get()除了会抛出ExecutionException,还会抛出InterruptedException(线程被中断时),所以首先要把这个异常也加上捕获,避免遗漏场景。
2. 解析实际的FCM异常
从ExecutionException中取出cause,然后判断它属于哪个FCM专属异常类(这些类都是FirebaseMessagingException的子类),不同的异常对应不同的处理逻辑:
RegistrationTokenNotRegisteredException:目标设备的token已经失效(比如APP卸载、token被注销),必须把这个token从你的存储中移除,避免后续重复推送浪费资源。InvalidApnsTokenException:APNs token格式不正确,检查你的token是否符合苹果的规范。MismatchSenderIdException:你的sender ID和token对应的sender ID不匹配,确认项目配置的sender ID是否正确。QuotaExceededException:超出了FCM的推送配额,需要等配额重置或者升级套餐。UnavailableException:FCM服务暂时不可用,属于临时性错误,可以考虑重试推送。
3. 完整代码示例
Message message = buildMessage(token, payload); try { String response = FirebaseMessaging.getInstance().sendAsync(message).get(); LOGGER.info("Successfully sent message: " + response); } catch (InterruptedException e) { // 线程被中断的处理逻辑 LOGGER.error("Push notification thread interrupted: ", e); // 恢复中断状态(可选但推荐,避免后续线程状态异常) Thread.currentThread().interrupt(); } catch (ExecutionException e) { Throwable actualException = e.getCause(); if (actualException instanceof RegistrationTokenNotRegisteredException) { LOGGER.error("Token is no longer registered, remove it from database: ", actualException); // 这里添加移除无效token的业务逻辑 } else if (actualException instanceof InvalidApnsTokenException) { LOGGER.error("Invalid APNs token format: ", actualException); // 校验token格式或通知客户端重新获取有效token } else if (actualException instanceof MismatchSenderIdException) { LOGGER.error("Sender ID mismatch with token: ", actualException); // 检查项目sender ID配置是否正确 } else if (actualException instanceof QuotaExceededException) { LOGGER.error("FCM quota exceeded: ", actualException); // 处理配额超限逻辑,比如告警或调整推送频率 } else if (actualException instanceof UnavailableException) { LOGGER.warn("FCM service unavailable, consider retrying: ", actualException); // 实现重试机制(比如用定时任务或指数退避重试) } else if (actualException instanceof FirebaseMessagingException) { // 其他FCM相关的通用异常处理 LOGGER.error("FCM messaging error: ", actualException); } else { // 非FCM的异常,比如网络问题、序列化错误等 LOGGER.error("Unexpected error during push notification: ", actualException); } }
额外提示
- 对于
UnavailableException这类临时性错误,建议实现指数退避重试机制,避免短时间内重复请求导致更多错误。 - 所有FCM异常都可以通过
getErrorCode()方法获取错误码字符串,方便日志统计或后续的自动化处理。
内容的提问来源于stack exchange,提问作者Germs
相关产品推荐
相关产品推荐

