推送通知

Firebase Cloud Messaging 设置、Token 注册和登录态测试推送。

Kotlin 模板使用 Firebase Cloud Messaging 做 Android 推送,并用 Supabase device_tokens 表和登录态 Edge Function 做测试推送。没有 Firebase 配置时应用仍可构建; 放入 app/google-services.json 并由登录用户注册 token 后,FCM 才会启用。

提供什么

Surface状态实现
FCM 接收服务IntegratedFcmService 继承 FirebaseMessagingService
运行时权限IntegratedNotificationsScreen 在 Android 13+ 请求 POST_NOTIFICATIONS
Token 注册IntegratedNotificationsRepository upsert 到 public.device_tokens
应用内 inboxExample收到的消息保存在内存 StateFlow,最多 50 条
测试发送器Integratedsend-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.json

app/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_idtokenplatform = androidprovider = 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_IDFIREBASE_CLIENT_EMAILFIREBASE_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 = false

migration 中有匹配的 (user_id, token) 唯一约束,所以重复注册同一个 token 会刷新 updated_atlast_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 后会清空。

用应用测试

  1. 使用真机或带 Play services 的模拟器镜像。
  2. 登录应用。
  3. 打开 Me -> Notifications
  4. Android 弹窗出现时授予 POST_NOTIFICATIONS
  5. 点击 Enable push notifications
  6. 点击 Send test push
  7. 确认系统通知出现,并且应用内 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 = androidprovider = fcm 的行,并使用服务端 Firebase Admin 凭据发送。

自定义

  • 如果通知是产品功能,把内存 inbox 换成持久化表。
  • 如果不想保留旧行,退出登录时添加 token 清理。
  • 只有在同步扩展 RLS 策略后,才给 device_tokens 添加 topic、segment 或 campaign 元数据。
  • Firebase Admin 凭据必须放在 Supabase Edge secrets,不能放进 APK。

相关页面

On this page