Xamarin.Forms iOS点击非活跃状态推送通知后应用卡在启动屏问题
问题成因
- 启动流程与推送跳转逻辑互锁:自定义视频启动屏需要等播放完成才会将UIWindow的根控制器切换为Xamarin.Forms应用的主控制器,前台点击推送后存储的全局待跳转标记/推送数据未在应用销毁时清空,冷启动时
FinishedLaunching读取到残留标记直接执行跳转逻辑,但此时主控制器还未初始化完成,两个逻辑互锁导致卡死。 - UIWindow层级管理混乱:部分视频启动屏实现方案会单独创建新的UIWindow层级覆盖在主Window之上播放视频,推送跳转逻辑默认向主Window的导航栈Push页面,此时主Window被启动屏Window遮挡、且跳转执行时启动屏Window未被正确释放,会出现主页面加载完成但被遮挡,或跳转找不到有效导航栈的白屏问题。
- 应用状态判断错误:应用非活跃状态收到推送后点击图标启动时,若错误将该场景判定为需要处理推送跳转,未判断启动流程是否完成就直接执行跳转,启动屏的dismiss逻辑和页面跳转逻辑同时操作根控制器,会引发控制器层级异常导致卡顿。
解决方案
1. 统一启动状态标记与缓存清理机制
- 新增全局布尔标记
isLaunchCompleted,默认值为false,仅当视频启动屏播放完成、Xamarin.Forms主应用根控制器完全加载后才设为true。 - 应用退到后台或被销毁前,清空所有全局存储的待跳转推送数据、跳转标记,避免冷启动读取到残留的无效数据。
- 新增推送数据有效性校验逻辑,冷启动时读取到的推送如果生成时间超过5分钟直接丢弃,不执行跳转。
2. 调整推送跳转的执行时机
- 封装所有推送跳转逻辑为独立方法
HandlePendingNotification(),禁止直接在FinishedLaunching方法中执行跳转。 - 冷启动时如果收到推送,先将推送数据存入全局变量缓存,等待
isLaunchCompleted变为true后,再延迟300毫秒执行跳转逻辑,确保主导航栈已完全初始化。 - 前台运行状态收到推送时,直接判断
isLaunchCompleted为true即可立即跳转,无需缓存。
3. 优化UIWindow层级与启动兜底逻辑
- 整个应用仅保留一个全局UIWindow实例,视频启动屏作为根控制器的子视图/子控制器展示,不要单独创建新的Window层级。
- 给视频启动屏增加3秒兜底超时逻辑,无论视频是否播放完成,3秒后强制dismiss启动屏,切换主应用根控制器并将
isLaunchCompleted设为true,避免启动流程永久卡死。
代码示例(AppDelegate.cs修改参考)
public static class GlobalConfig { public static bool isLaunchCompleted = false; public static NSDictionary PendingNotification = null; } [Register("AppDelegate")] public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate { public override bool FinishedLaunching(UIApplication app, NSDictionary options) { global::Xamarin.Forms.Forms.Init(); Window = new UIWindow(UIScreen.MainScreen.Bounds); // 初始化视频启动屏控制器 var videoSplashVC = new VideoSplashViewController(); Window.RootViewController = videoSplashVC; Window.MakeKeyAndVisible(); // 缓存冷启动推送数据,暂不处理 if (options?.ContainsKey(UIApplication.LaunchOptionsRemoteNotificationKey) == true) { GlobalConfig.PendingNotification = options[UIApplication.LaunchOptionsRemoteNotificationKey] as NSDictionary; } // 启动屏完成回调 videoSplashVC.OnSplashFinished += () => { // 加载Xamarin.Forms主应用 LoadApplication(new App()); GlobalConfig.isLaunchCompleted = true; // 延迟处理待跳转推送 DispatchQueue.MainQueue.DispatchAfter( new DispatchTime(DispatchTime.Now, 300 * TimeSpan.NanosecondsPerMillisecond), () => { if (GlobalConfig.PendingNotification != null) { HandlePendingNotification(GlobalConfig.PendingNotification); GlobalConfig.PendingNotification = null; } }); }; return base.FinishedLaunching(app, options); } private void HandlePendingNotification(NSDictionary notificationData) { // 你的原有推送跳转逻辑 } }
内容的提问来源于stack exchange,提问作者AshwiniPatkar
相关产品推荐
相关产品推荐

