Flutter中Firebase Messaging后台处理器终止状态下API调用失败
应用终止状态下Firebase后台消息处理器中API调用卡住的解决方案
我开发了一款聊天消息应用,支持用户在前台、后台或终止状态下通过推送通知回复消息。但当应用处于终止状态时,firebaseMessagingBackgroundHandler中的API调用无法正常工作,程序卡在sendNotification函数处。
问题代码
后台事件处理代码
Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async { await GetStorage.init(); await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform); //Call HTTP request <Stuck here sendNotification( personUid, title, body, notificationTypeId, chatRoomId, userTokenDummy, userToken, serverKey, currentUserId, currentUserToken, ); }
API请求代码
sendNotification({ required String personUid, required String title, required String body, required int notificationTypeId, String? chatRoomId, String? userTokenDummy, String? userToken, String? serverKey, String? currentUserId, String? currentUserToken, }) async { try { final response = await http.post( Uri.parse('https://fcm.googleapis.com/fcm/send'), headers: <String, String>{ HttpHeaders.contentTypeHeader: 'application/json', HttpHeaders.authorizationHeader: 'key=$serverKey' }, body: jsonEncode( <String, dynamic>{ "data": <String, dynamic>{ "title": title, "body": body, "click_action": "FLUTTER_NOTIFICATION_CLICK", "id": "1", "status": "done", "senderId": currentUserId, "senderToken": currentUserToken, "notificationTypeId": notificationTypeId, "chatRoomId": chatRoomId, }, "android": { "priority": "high", }, "apns": { "headers": {"apns-priority": "10"} }, "to": userToken, "content_available": true, "mutable-content": 1, "priority": "high", }, ), ); return response; } catch (e) { console(e.toString()); } }
可能的原因
- 系统后台限制:Android和iOS在应用终止状态下,对后台进程的网络访问、资源占用有严格管控,HTTP请求可能被系统拦截或无法获取足够资源完成请求。
- 初始化阻塞:后台处理器中调用
GetStorage.init()和Firebase.initializeApp()可能因为文件系统访问或异步初始化问题导致阻塞,进而卡住后续API调用。 - 无超时机制:HTTP请求未设置超时时间,一旦网络环境不佳或请求目标无响应,会无限等待导致进程挂起。
修复方案
1. 简化后台初始化逻辑
避免在后台处理器中依赖本地存储(如GetStorage),直接从推送消息的data字段获取必要参数;同时优化Firebase初始化逻辑,避免重复初始化。
2. 给HTTP请求添加超时
为http.post设置合理的超时时间,防止请求无限阻塞。
3. 完善错误捕获与日志
确保所有异常都被捕获并输出日志,便于排查问题。
4. 配置必要的后台权限
- Android:在
AndroidManifest.xml中添加FOREGROUND_SERVICE权限,并确保FCM消息设置为高优先级。 - iOS:在Xcode中开启
Background Modes中的Remote notifications选项。
修改后的代码示例
优化后的后台事件处理器
Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async { // 从推送消息的data字段提取所需参数,避免依赖GetStorage final data = message.data; final personUid = data['personUid'] ?? ''; final title = data['title'] ?? ''; final body = data['body'] ?? ''; final notificationTypeId = int.tryParse(data['notificationTypeId'] ?? '0') ?? 0; final chatRoomId = data['chatRoomId']; final userToken = data['userToken']; final serverKey = data['serverKey']; final currentUserId = data['currentUserId']; final currentUserToken = data['currentUserToken']; // 仅当Firebase未初始化时才执行初始化 if (Firebase.apps.isEmpty) { await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform, ); } try { // 添加超时机制,避免请求无限阻塞 await sendNotification( personUid: personUid, title: title, body: body, notificationTypeId: notificationTypeId, chatRoomId: chatRoomId, userToken: userToken, serverKey: serverKey, currentUserId: currentUserId, currentUserToken: currentUserToken, ).timeout(const Duration(seconds: 10)); } catch (e) { print('后台消息处理失败: $e'); } }
优化后的API请求函数
Future<http.Response?> sendNotification({ required String personUid, required String title, required String body, required int notificationTypeId, String? chatRoomId, String? userTokenDummy, String? userToken, String? serverKey, String? currentUserId, String? currentUserToken, }) async { try { final response = await http.post( Uri.parse('https://fcm.googleapis.com/fcm/send'), headers: <String, String>{ HttpHeaders.contentTypeHeader: 'application/json', HttpHeaders.authorizationHeader: 'key=$serverKey', }, body: jsonEncode( <String, dynamic>{ "data": <String, dynamic>{ "title": title, "body": body, "click_action": "FLUTTER_NOTIFICATION_CLICK", "id": "1", "status": "done", "senderId": currentUserId, "senderToken": currentUserToken, "notificationTypeId": notificationTypeId, "chatRoomId": chatRoomId, }, "android": {"priority": "high"}, "apns": {"headers": {"apns-priority": "10"}}, "to": userToken, "content_available": true, "mutable-content": 1, "priority": "high", }, ), ).timeout(const Duration(seconds: 10)); // 设置10秒超时 return response; } catch (e) { print('发送通知失败: $e'); return null; } }
内容的提问来源于stack exchange,提问作者Jitendra Mistry
相关产品推荐
相关产品推荐

