Android平台Xamarin Forms结合MvvmCross导航后空白页问题求助
Xamarin.Forms + MvvmCross 后台恢复后导航空白页问题解决
问题还原
我之前也碰到过几乎一模一样的问题!用Xamarin.Forms搭MvvmCross开发Android应用时,在Master Detail页和普通页面之间来回导航都正常,但只要把应用切后台再恢复,再执行导航操作就会跳出个啥UI都没有的空白页,关键是连个异常都抛不出来,排查起来特别头疼。你的复现步骤也完全匹配:
- 启动应用,默认加载Master Detail页面
- 从Master Detail页导航到普通ContentPage
- 按Home键把应用切后台
- 再切回前台恢复应用
- 尝试从当前ContentPage导航回Master Detail页,或者去其他页面,空白页就出现了
问题根源分析
折腾了好一阵才摸清楚,这大概率是Android系统后台回收资源搞的鬼:应用后台时,系统可能会把页面相关的上下文资源回收,而MvvmCross的导航系统在恢复后没正确重建页面的关联关系——比如Master Detail页的Detail容器丢了引用,或者导航栈的状态直接丢了,导致导航时找不到正确的容器去加载目标页面,自然就显示空白了。
实测有效的解决方案
方案1:给Master Detail页加状态恢复逻辑
在你的Master Detail页对应的ViewModel里,重写ViewAppearing方法,每次页面显示(包括后台恢复后)都检查一下Detail容器的状态,要是发现异常就重新初始化:
public override async void ViewAppearing() { base.ViewAppearing(); var masterDetailPage = Xamarin.Forms.Application.Current.MainPage as MasterDetailPage; // 检查Detail是否为空,或者是不是有效的导航页 if (masterDetailPage?.Detail == null || !(masterDetailPage.Detail is NavigationPage)) { // 重新设置Detail为导航页,绑定到首页ViewModel masterDetailPage.Detail = new NavigationPage(await ViewModel.NavigationService.Navigate<HomeViewModel>()); } }
方案2:自定义导航服务兜底
如果方案1还不稳,那就整个自定义导航服务,重写导航方法,每次导航前先检查页面状态,异常的话先修复再导航:
public class CustomMvxNavigationService : MvxNavigationService { public override async Task Navigate<TViewModel>(object parameter = null, MvxRequestedBy requestedBy = null) { var currentActivity = Mvx.IoCProvider.Resolve<IMvxAndroidCurrentTopActivity>().Activity; // 判断应用是不是刚从后台恢复 if (!currentActivity.IsFinishing && currentActivity.Window.DecorView.Visibility == ViewStates.Visible) { var mainPage = Xamarin.Forms.Application.Current.MainPage; if (mainPage is MasterDetailPage masterDetail && masterDetail.Detail == null) { // 先修复MasterDetail的Detail容器 masterDetail.Detail = new NavigationPage(await base.Navigate<HomeViewModel>()); } } await base.Navigate<TViewModel>(parameter, requestedBy); } }
然后在App.cs里替换默认的导航服务:
protected override void InitializeFirstChance() { base.InitializeFirstChance(); Mvx.IoCProvider.RegisterType<IMvxNavigationService, CustomMvxNavigationService>(); }
方案3:利用Android生命周期保存导航状态
还可以在Android的MainActivity里,通过生命周期方法保存当前导航的ViewModel状态,恢复时重新导航回去:
protected override void OnSaveInstanceState(Bundle outState) { base.OnSaveInstanceState(outState); // 保存当前显示的ViewModel类型全名 var currentVm = Mvx.IoCProvider.Resolve<IMvxNavigationService>().CurrentViewModel; if (currentVm != null) { outState.PutString("ActiveViewModel", currentVm.GetType().FullName); } } protected override void OnRestoreInstanceState(Bundle savedInstanceState) { base.OnRestoreInstanceState(savedInstanceState); if (savedInstanceState.ContainsKey("ActiveViewModel")) { var vmType = Type.GetType(savedInstanceState.GetString("ActiveViewModel")); // 恢复到之前的ViewModel页面 _ = Mvx.IoCProvider.Resolve<IMvxNavigationService>().Navigate(vmType); } }
验证方式
按照你之前的复现步骤再走一遍:启动→导航到普通页→切后台→恢复→导航,应该就能正常显示目标页面,不会再出空白页了。
内容的提问来源于stack exchange,提问作者grinh
相关产品推荐
相关产品推荐

