PHP实现向所有Android设备发送Firebase推送通知
代码问题排查
你写的代码存在以下几个直接导致推送失效的问题:
- 认证头格式错误:
Authorization字段中key=和你的服务器密钥之间多了空格,FCM接口会直接返回401认证失败 - 参数位置与类型错误:
priority、content_available是推送请求的顶层参数,你错误放到了data数组里;且priority传了数字值10,FCM接口仅接受字符串类型的high/normal,数字值会被直接丢弃,导致推送优先级过低被Android系统拦截 - 冗余无效字段:
notification数组里的alert是iOS APNs专属字段,Android端无识别逻辑,多余字段可能触发接口解析异常 - 全量推送逻辑缺失:你指定推送到
/topics/alerts主题,但没有保证所有安装App的Android设备都订阅了该主题,未订阅设备完全收不到消息,自然无法实现全量推送 - 无错误捕获逻辑:代码没有打印curl请求错误、FCM接口返回的错误信息,出问题后无法定位根因
全量Android设备推送实现方案
实现全量推送成本最低的方案是主题推送:所有Android设备启动App时固定订阅同一个全局主题,服务端直接向该主题发消息即可触达所有设备,不需要自行维护设备Token列表。
客户端前置配置
在Flutter端Firebase Messaging初始化逻辑中,添加全局主题订阅代码,确保每个用户启动App都会执行:
// 放在Firebase初始化完成后执行即可 await FirebaseMessaging.instance.subscribeToTopic('all_android_devices');
如果你已经创建了Android高优先级通知渠道,把渠道ID记下来,后面PHP代码里要对应上。
可用PHP推送代码
<?php // FCM旧版HTTP接口地址,目前仍可正常使用 $fcmEndpoint = 'https://fcm.googleapis.com/fcm/send'; // 替换为Firebase控制台-项目设置-云消息传递页面下的「旧版服务器密钥」,不要加多余空格 $serverKey = '替换成你自己的服务器密钥'; // 通知栏展示的内容 $notificationPayload = [ "title" => "通知标题", "body" => "通知正文内容", "sound" => "default", // 如果你创建了自定义高优先级通知渠道,把下面这行的注释打开,值替换成你的渠道ID // "android_channel_id" => "high_importance_channel" ]; // 透传自定义数据,App前后台都能拿到,可做点击跳转、业务逻辑处理 $dataPayload = [ "click_action" => "FLUTTER_NOTIFICATION_CLICK", // Flutter固定的通知点击动作标识,必须加否则点击通知无响应 "redirect" => "/home", // 自定义字段示例:点击通知跳转到指定页面 "msg_id" => uniqid() // 自定义消息唯一标识,可用于去重 ]; // 完整请求体 $requestBody = [ 'to' => '/topics/all_android_devices', // 对应客户端订阅的全局主题 'notification' => $notificationPayload, 'data' => $dataPayload, 'priority' => 'high', // 高优先级,Android端会尝试即时唤醒推送 'content_available' => true, 'android' => [ 'priority' => 'high', 'notification' => [ 'default_sound' => true, 'default_vibrate_timings' => true ] ] ]; $headers = [ 'Authorization: key=' . $serverKey, // *注意key=后直接拼接密钥,不能有空格* 'Content-Type: application/json' ]; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $fcmEndpoint); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); curl_setopt($ch, CURLOPT_TIMEOUT, 30); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($requestBody)); $response = curl_exec($ch); // 捕获curl层错误 if (curl_errno($ch)) { echo "请求发送失败:" . curl_error($ch); exit; } curl_close($ch); // 处理接口返回结果 $result = json_decode($response, true); if (isset($result['success']) && $result['success'] === 1) { echo "推送成功,消息ID:" . $result['message_id']; } else { echo "推送失败,接口返回:" . print_r($result, true); } ?>
注意事项
- 不要使用新版FCM HTTP v1接口的服务账号凭据做旧接口认证,必须用页面上标注的「旧版服务器密钥」
- 主题消息最长延迟为30秒,属于FCM官方正常策略,不是代码问题
- 用户清除App数据、卸载重装后,首次启动App会自动重新订阅全局主题,不需要额外处理
- 如果需要定向推送给部分用户,再额外给用户打不同的主题标签即可
- 不要在
data字段中存放敏感信息,透传数据为明文传输
内容的提问来源于stack exchange,提问作者thekavak
相关产品推荐
相关产品推荐

