为什么不能直接从服务器推送到手机?
要理解一个核心问题:为什么不能让 PHP 服务器直接连接到每一台 Android 手机并发送消息?

答案是:手机处在 NAT(网络地址转换)和防火墙之后,没有公网 IP,且 IP 地址是动态变化的。 这就像一个没有固定地址、不对外开门的房间,服务器找不到这个房间,也无法敲门进去。
我们必须需要一个“中间人”或“邮局”来帮忙传递消息,这个“中间人”Google 的 Firebase Cloud Messaging (FCM) 服务。
技术架构:三驾马车
整个推送系统的架构由三个核心部分组成:
- 你的 PHP 服务器 (后端):负责业务逻辑,有新订单了”、“用户收到新评论”,当这些事件发生时,它告诉 FCM 服务:“请帮我把这条消息推送给用户 A”。
- Firebase Cloud Messaging (FCM) (服务):Google 提供的免费推送服务,它像一个全球性的邮局,负责接收你服务器的请求,并将消息准确、可靠地送达目标用户的手机上。
- 你的 Android App (客户端):手机上的应用程序,它需要先向 FM 注册,获取一个唯一的“设备令牌 (Device Token)”,并将这个令牌发送给你的 PHP 服务器,它负责接收并处理 FCM 转发过来的消息。
流程图如下:

+-------------------+ 1. 注册获取Token +--------------------------+ 2. 上传Token +-------------------+
| Android App | -------------------------> | PHP 服务器 | <-------------------- | |
| (运行在手机上) | <------------------------- | (存储每个用户的Device Token) | ----------------------> | |
+-------------------+ 4. 推送消息 +--------------------------+ 3. 发送推送请求 | |
^ | | |
| 5. 接收并显示消息 | 6. 请求发送消息 | |
| V | |
+----------------------------------------------------+-----------------------------------+ |
| 7. 转发消息 |
V |
+--------------------------+ |
| Firebase FCM | <---------------------------------+
| (Google 的推送服务) |
+--------------------------+
详细实现步骤
我们将分步讲解如何搭建这个系统。
第一步:配置 Firebase 项目 (后端准备)
- 创建 Firebase 项目:
- 访问 Firebase 控制台。
- 点击“创建项目”,填写项目名称。
- 添加 Android App 到 Firebase 项目:
- 在项目概览页面,点击 Android 图标 (🤖)。
- 输入你的 Android App 的包名 (
com.example.myapp),你可以在app/build.gradle文件中找到它。 - 按照提示下载
google-services.json文件,并将其复制到你 Android 项目的app目录下。
- 获取服务器密钥:
- 在 Firebase 控制台的左侧菜单,进入 设置 -> 项目设置。
- 在 云消息传递 标签页下,你会找到 “Web API 密钥” 和 “服务器密钥”。
- 复制“服务器密钥”,这个密钥稍后会在 PHP 代码中使用,用于验证你的服务器身份。
第二步:配置 Android App (客户端准备)
-
添加依赖:
-
在项目级
build.gradle文件中,添加google-services插件:buildscript { dependencies { // ... classpath 'com.google.gms:google-services:4.3.15' // 使用最新版本 } } -
在应用级
build.gradle文件中,应用插件并添加 FCM 依赖:
(图片来源网络,侵删)// 在文件顶部 apply plugin: 'com.android.application' apply plugin: 'com.google.gms.google-services' // 应用插件 dependencies { // ... implementation 'com.google.firebase:firebase-messaging:23.1.2' // 使用最新版本 }
-
-
获取 Device Token 并发送给服务器:
-
创建一个服务来处理 FCM 的生命周期事件。
-
MyFirebaseMessagingService.java:import com.google.firebase.messaging.FirebaseMessagingService; import com.google.firebase.messaging.RemoteMessage; public class MyFirebaseMessagingService extends FirebaseMessagingService { @Override public void onNewToken(String token) { // 当 App 首次启动或 Token 刷新时,此方法被调用 // token 就是设备的唯一标识符 sendRegistrationToServer(token); } @Override public void onMessageReceived(RemoteMessage remoteMessage) { // 当手机在线且 App 在前台时,收到消息会调用此方法 // App 在后台,消息会由系统通知栏处理 sendNotification(remoteMessage.getNotification().getTitle(), remoteMessage.getNotification().getBody()); } private void sendRegistrationToServer(String token) { // TODO: 将这个 token 发送到你的 PHP 服务器 // 你可以使用 Retrofit, OkHttp 或 Volley 等网络库 // 示例:使用 Volley // String url = "https://your-php-server.com/save_token.php"; // Map<String, String> params = new HashMap<>(); // params.put("token", token); // VolleySingleton.getInstance(this).addToRequestQueue(new CustomPostRequest(url, params, ...)); System.out.println("Device Token: " + token); } private void sendNotification(String title, String body) { // 创建并显示一个本地通知 // ... (使用 NotificationCompat.Builder) } } -
别忘了在
AndroidManifest.xml中注册这个服务:<service android:name=".MyFirebaseMessagingService" android:exported="false"> <intent-filter> <action android:name="com.google.firebase.MESSAGING_EVENT" /> </intent-filter> </service>
-
第三步:PHP 服务器实现 (后端核心)
PHP 服务器主要做两件事:
-
接收并存储 Android App 发来的 Device Token。
-
当需要推送时,调用 FCM API 发送请求。
-
安装 HTTP 客户端库: 推荐使用
guzzlehttp/guzzle,它能让代码更简洁。composer require guzzlehttp/guzzle
-
编写接收 Token 的脚本 (
save_token.php): 这个脚本很简单,接收一个 POST 请求,将 Token 存储到你的数据库中(MySQL)。<?php // save_token.php header('Content-Type: application/json'); $response = ['status' => 'error', 'message' => 'Invalid request']; if ($_SERVER['REQUEST_METHOD'] === 'POST') { $token = $_POST['token'] ?? null; $userId = $_POST['user_id'] ?? null; // 假设你关联了用户ID if ($token && $userId) { // TODO: 将 $token 和 $userId 存储到你的数据库中 // INSERT INTO device_tokens (user_id, token) VALUES (?, ?) ON DUPLICATE KEY UPDATE token = VALUES(token) // 模拟存储成功 $response['status'] = 'success'; $response['message'] = 'Token saved successfully.'; } else { $response['message'] = 'Token or User ID is missing.'; } } echo json_encode($response); ?> -
编写推送消息的脚本 (
send_push_notification.php): 这是 PHP 代码的核心,它负责构建并发送请求到 FCM。<?php // send_push_notification.php require 'vendor/autoload.php'; // 引入 Guzzle use GuzzleHttp\Client; use GuzzleHttp\Exception\RequestException; // 从你的数据库中获取要推送的用户的 Device Token // 这里我们为了演示,直接写死一个 token $deviceToken = 'YOUR_DEVICE_TOKEN_HERE'; // 替换成从数据库获取的真实 token $serverKey = 'YOUR_FCM_SERVER_KEY_HERE'; // 替换成你在 Firebase 控制台获取的 Server Key if (empty($deviceToken) || empty($serverKey)) { die('Device Token or Server Key is not set.'); } $client = new Client(); $url = 'https://fcm.googleapis.com/v1/projects/YOUR_PROJECT_ID/messages:send'; // 替换成你的项目ID $headers = [ 'Authorization
