Flutter iOS前台Firebase Messaging调用Awesome Notifications崩溃
iOS前台触发Awesome Notifications创建导致崩溃问题修复
问题现象
集成Firebase Messaging与Awesome Notifications的Flutter应用,在Android全场景、iOS后台场景下通知功能均可正常运行,但iOS端处于前台时,收到消息触发通知创建逻辑就会发生崩溃。崩溃点位于FirebaseMessaging.onMessage.listen监听器回调内,移除回调中Awesome Notifications的通知创建代码后,前台逻辑恢复正常。
崩溃报错栈核心信息:
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=2, address=0x16f793ff8) frame #0: 0x0000000188cfd7dc libswiftCore.dylib`swift_conformsToProtocolMaybeInstantiateSuperclasses(swift::TargetMetadata<swift::InProcess> const*, swift::TargetProtocolDescriptor<swift::InProcess> const*, bool) + 44
问题原因
- 初始化顺序错误:原代码中
AwesomeNotifications().initialize的调用位置在所有Firebase消息监听注册、权限申请逻辑之后,iOS端前台收到消息时,Awesome Notifications的原生通道、通知配置还未完成初始化,此时调用创建通知方法会触发原生侧非法内存访问。 - iOS通知代理冲突:原AppDelegate实现中手动将
UNUserNotificationCenter.current().delegate设置为当前AppDelegate实例,覆盖了Awesome Notifications插件原生侧注册的通知中心代理。插件在前台处理通知时,会调用代理对应的协议方法,由于代理被替换,出现协议方法不匹配、野指针调用问题,最终触发Swift层协议符合性检查的崩溃,和报错栈中swift_conformsToProtocolMaybeInstantiateSuperclasses的错误完全对应。 - 上下文风险:原代码在异步消息回调中使用
UniqueKey().hashCode作为通知ID,UniqueKey是Flutter Widget体系的类,脱离Widget上下文使用存在生成无效ID、ID重复的潜在风险。
修复步骤
- 调整初始化顺序:将
AwesomeNotifications().initialize调用移动到WidgetsFlutterBinding.ensureInitialized()、Firebase.initializeApp()执行完成后,所有Firebase消息监听注册、权限申请逻辑之前,确保通知插件在任何可能触发通知创建的逻辑运行前就完成原生侧初始化。 - 移除AppDelegate中手动设置通知中心代理的代码:删除
if #available(iOS 10.0, *) { UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate }这段代码,Awesome Notifications会自动接管通知中心的前台回调代理,无需手动设置。 - 替换通知ID生成逻辑:不要在全局异步回调中使用
UniqueKey().hashCode作为通知ID,改用基于消息唯一标识生成的int值、或单独维护的自增ID作为通知id,避免无效ID问题。 - 可选补充:调用
createNotification前,先通过AwesomeNotifications().isNotificationAllowed()校验通知权限状态,避免权限未就绪时触发创建逻辑。
修正后关键代码示例
Flutter端初始化逻辑
void main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); // 提前初始化Awesome Notifications AwesomeNotifications().initialize('resource://drawable/logo', [ NotificationChannel( channelGroupKey: 'basic_tests', channelKey: 'basic_channel', channelName: 'Basic notifications', channelDescription: 'Notification channel for basic tests', defaultColor: Color(0xFF9D50DD), ledColor: Colors.white, importance: NotificationImportance.High), ]); FirebaseMessaging _messaging = FirebaseMessaging.instance; FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); // 后续权限申请、消息监听逻辑可保持原有实现 NotificationSettings settings = await _messaging.requestPermission( alert: true, announcement: false, badge: true, carPlay: false, criticalAlert: false, provisional: false, sound: true, ); // 其余原有逻辑... runApp(const MyApp()); }
iOS端AppDelegate修正
import UIKit import Flutter import GoogleMaps @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { application.registerForRemoteNotifications() GMSServices.provideAPIKey("xxxx-xxxx") GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } }
内容的提问来源于stack exchange,提问作者PeakGen
相关产品推荐
相关产品推荐

