Flutter+Laravel安卓端无Firebase离线推送方案咨询
实现方案
核心前提说明:Flutter应用进程完全被杀死的场景下,应用自身维持的WebSocket/MQTT等所有长连接都会被系统强制回收,没有任何应用层保活手段可以100%绕过系统限制持续监听消息。要实现杀进程后仍可接收服务端推送,必须接入安卓、iOS官方提供的系统级推送通道,由系统服务统一托管推送接收链路,这是唯一稳定合规的实现路径。
完整链路逻辑:PostgreSQL新增数据 → Laravel捕获数据变更事件 → Laravel向系统推送服务提交推送请求 → 系统推送通道将消息下发到用户设备 → 系统唤起Flutter应用处理消息/展示通知。
后端(Laravel + PostgreSQL)配置步骤
- 监听PostgreSQL数据新增事件
如果所有业务数据写入都走Laravel模型层,直接利用Laravel自带的模型事件触发推送逻辑即可,不需要额外轮询数据库:
如果存在绕过Laravel直接写入PostgreSQL的场景,可以开启PostgreSQL的// 在需要监听的业务模型(例如订单、通知模型)的booted方法中注册创建事件 protected static function booted() { static::created(function ($model) { // 将推送任务丢入异步队列执行,避免阻塞主业务请求 dispatch(new SendNewDataPushJob($model))->onQueue('push'); }); }pg_notify扩展,配合Laravel常驻进程捕获数据库的NOTIFY信号触发推送,不需要定时扫库。 - 配置推送通道依赖
- 海外场景直接集成FCM通道,执行
composer require laravel-notification-channels/fcm安装对应依赖,iOS端APNs直接复用Laravel内置的推送配置,在配置文件中填写好APNs的认证密钥/证书即可。 - 国内安卓场景必须集成厂商聚合推送SDK(个推、极光等均可),不要仅依赖FCM——国内定制安卓系统会默认杀除系统白名单外的所有后台进程,FCM服务在大部分国内机型上无法常驻,聚合SDK已经适配了华为、小米、OPPO、vivo、荣耀等所有主流厂商的系统级推送通道,不需要逐个对接。
- 海外场景直接集成FCM通道,执行
- 实现推送发送逻辑
提前建好用户设备表,存储每个用户登录Flutter端时上报的设备推送Token,推送任务执行时按用户对应的设备类型和Token分通道下发消息即可:// SendNewDataPushJob任务的handle方法 public function handle() { // 查询所有需要接收通知的用户,关联取到对应的设备推送Token $notifyUsers = User::whereHas('devices')->get(); // 批量发送推送,通知栏消息直接由系统展示,透传消息可触发应用后台逻辑 Notification::send($notifyUsers, new NewDataCreatedNotification($this->bizData)); } - 守护队列进程
推送逻辑必须走异步队列,用Supervisor守护php artisan queue:work --queue=push进程常驻,避免同步发送推送导致业务接口超时。
Flutter端配置步骤
- 集成对应推送插件
海外场景直接用firebase_messaging插件对接FCM和APNs,国内场景额外接入对应聚合推送的Flutter官方插件即可,这类插件已经封装好了系统通道适配、杀进程消息接收的逻辑,不需要自己写后台保活服务。 - 初始化推送逻辑,覆盖全场景回调
在应用启动入口初始化推送服务,覆盖前台运行、后台挂起、进程被杀三种状态的消息接收逻辑:Future<void> initPushService() async { // 申请通知权限,iOS必须调用,安卓13及以上版本也需要动态申请 await FirebaseMessaging.instance.requestPermission(); // 获取当前设备的推送Token,调用Laravel接口上报,和当前登录用户绑定 String? deviceToken = await FirebaseMessaging.instance.getToken(); if (deviceToken != null) { await ApiClient.uploadPushToken(deviceToken); } // 监听Token刷新事件,Token变更时重新上报,避免Token过期导致推送失败 FirebaseMessaging.instance.onTokenRefresh.listen((newToken) { ApiClient.uploadPushToken(newToken); }); // 注册应用完全被杀时的后台消息处理器,该处理器需要是顶级函数 FirebaseMessaging.onBackgroundMessage(backgroundPushHandler); // 监听应用前台运行时收到的消息 FirebaseMessaging.onMessage.listen((RemoteMessage msg) { // 前台状态系统不会自动弹通知,可配合flutter_local_notifications插件手动弹出通知,或直接刷新页面数据 }); // 监听应用后台挂起时,用户点击通知栏唤起应用的事件 FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage msg) { // 根据消息内容跳转到对应业务页面 }); // 处理应用完全关闭状态下,用户点击通知栏启动应用的场景 RemoteMessage? launchMsg = await FirebaseMessaging.instance.getInitialMessage(); if (launchMsg != null) { // 读取消息内容,做初始化路由跳转 } } // 顶级后台消息处理函数,不能写在类内部,需要添加vm:entry-point注解保证杀进程时能被系统找到 @pragma('vm:entry-point') Future<void> backgroundPushHandler(RemoteMessage msg) async { // 此处可执行轻量逻辑,比如存储消息到本地数据库、触发本地通知,不要执行耗时超过10秒的操作 await LocalDB.insertNotice(msg.data); } - 本地通知适配
应用在前台运行时系统不会自动弹出通知栏提醒,需要配合flutter_local_notifications插件,在前台收到消息时手动构建通知弹出,和系统通知样式保持一致。
避坑说明
- 不要浪费时间尝试用自建长连接实现杀进程推送,安卓、iOS的系统权限机制决定了非系统级应用不可能在进程被杀后维持网络连接。
- iOS端所有杀进程场景的推送必须走APNs,没有任何第三方方案可以绕过APNs实现推送。
- Laravel端发送推送时如果收到Token无效的返回,要及时删除数据库中对应的失效Token记录,避免产生大量无效请求。
- 后台消息处理函数中不要执行重度耗时操作,系统给后台唤起应用的执行时间有限,超时会被系统强制中断。
内容的提问来源于stack exchange,提问作者Emily Khyriem
相关产品推荐
相关产品推荐

