推送通知
Firebase Cloud Messaging 设置、Token 注册和登录态测试推送。
Kotlin 模板使用 Firebase Cloud Messaging 做 Android 推送,并用 Supabase
device_tokens 表和登录态 Edge Function 做测试推送。没有 Firebase 配置时应用仍可构建;
放入 app/google-services.json 并由登录用户注册 token 后,FCM 才会启用。
提供什么
| Surface | 状态 | 实现 |
|---|---|---|
| FCM 接收服务 | Integrated | FcmService 继承 FirebaseMessagingService |
| 运行时权限 | Integrated | NotificationsScreen 在 Android 13+ 请求 POST_NOTIFICATIONS |
| Token 注册 | Integrated | NotificationsRepository upsert 到 public.device_tokens |
| 应用内 inbox | Example | 收到的消息保存在内存 StateFlow,最多 50 条 |
| 测试发送器 | Integrated | send-test-push 用 FCM HTTP v1 和 Supabase Edge secrets 发送 |
FCM 需要 Google Play services。真机和带 Play services 的模拟器镜像都可以测试;本地 FCM 测试不需要付费的 Play developer account。
关键文件
设置
创建 Firebase 项目
为你的 Android 应用创建或复用 Firebase 项目。添加 app/build.gradle.kts 中的 Android
package name;模板默认是:
com.soarstarter.kotlin添加 google-services.json
从 Firebase Console 下载 Android client config,放到:
soar-kotlin/app/google-services.jsonapp/build.gradle.kts 只在该文件存在时应用 com.google.gms.google-services,所以 CI
和零 key 本地构建不会因为 Firebase 缺失而失败。
模板 README 提到 app/google-services.json.example,但当前模板树中没有这个示例文件;
请以 Firebase 下载的 JSON 为准。
应用 device-token migration
应用 supabase/migrations/20260508000001_create_device_tokens.sql。该表保存
user_id、token、platform = android、provider = fcm 和时间戳。RLS 只允许用户
读取、插入、更新、删除自己的行。
部署 send-test-push
部署登录态测试发送器:
supabase functions deploy send-test-push --project-ref <ref>supabase/config.toml 对此函数保留 verify_jwt = true,因为 Android 应用会以当前
Supabase 用户身份调用它。
添加 Firebase Admin secrets
在 Firebase Console 生成 service-account private key,然后保存为 Supabase Edge secret:
supabase secrets set FIREBASE_SERVICE_ACCOUNT_JSON='<service-account-json>' --project-ref <ref>如果多行 JSON 在 shell 中不方便,也可以使用拆分 secrets:FIREBASE_PROJECT_ID、
FIREBASE_CLIENT_EMAIL 和 FIREBASE_PRIVATE_KEY。
Token 链路
Firebase token
-> FcmService.onNewToken() or NotificationsRepository.refreshAndRegisterToken()
-> NotificationsRepository.registerTokenOrThrow()
-> SupabaseDeviceTokenRemoteDataSource.register()
-> upsert public.device_tokens on (user_id, token)NotificationsScreen 负责显式启用流程。Android 13 及以上会请求
Manifest.permission.POST_NOTIFICATIONS;更低版本会跳过运行时权限,直接请求 Firebase
token。
SupabaseDeviceTokenRemoteDataSource 使用 PostgREST upsert:
onConflict = "user_id,token"
defaultToNull = falsemigration 中有匹配的 (user_id, token) 唯一约束,所以重复注册同一个 token 会刷新
updated_at 和 last_seen_at,不会生成重复行。
当前代码会在显式启用和 Firebase 轮换 token 时注册 token,但退出登录时不会删除
device_tokens。Edge Function 通过调用者的 RLS 读取数据,所以已退出用户不能发送测试
推送;不过旧 token 行会保留,直到你添加显式清理。
接收和打开推送
FcmService.onMessageReceived() 接受 FCM notification payload 或 data keys:
{
"title": "Soar Starter test push",
"body": "Your Android FCM setup is working.",
"url": "soar://notifications"
}服务会把 AppNotification 写入内存 inbox,并在 default channel 上发布系统通知。若
payload 中包含 url,点击通知会用 ACTION_VIEW 和该 URI 启动 MainActivity,然后
DeepLinkHandler 通过常规 deep-link resolver 转发。
inbox 是故意简化的示例状态:只保留当前进程最近 50 条通知,进程结束或用户点击 Clear 后会清空。
用应用测试
- 使用真机或带 Play services 的模拟器镜像。
- 登录应用。
- 打开 Me -> Notifications。
- Android 弹窗出现时授予
POST_NOTIFICATIONS。 - 点击 Enable push notifications。
- 点击 Send test push。
- 确认系统通知出现,并且应用内 inbox 新增一行。
NotificationsRepository.sendTestPush() 会在用户未登录或尚未注册 push token 时拒绝发送。
用 curl 测试
使用同一个已注册设备 token 账号的 Supabase access token:
curl -i \
-X POST https://<ref>.supabase.co/functions/v1/send-test-push \
-H "Authorization: Bearer <supabase-access-token>" \
-H "Content-Type: application/json" \
--data '{
"title": "Soar Starter test push",
"body": "Your Android FCM setup is working.",
"url": "soar://notifications"
}'函数从不接受请求体中的 device token。它验证调用者,通过 RLS 读取该用户
platform = android 且 provider = fcm 的行,并使用服务端 Firebase Admin 凭据发送。
自定义
- 如果通知是产品功能,把内存 inbox 换成持久化表。
- 如果不想保留旧行,退出登录时添加 token 清理。
- 只有在同步扩展 RLS 策略后,才给
device_tokens添加 topic、segment 或 campaign 元数据。 - Firebase Admin 凭据必须放在 Supabase Edge secrets,不能放进 APK。
