【问题标题】:MvvmCross ViewModel caching and re-initializingMvvmCross ViewModel 缓存和重新初始化
【发布时间】:2013-07-25 07:37:57
【问题描述】:

当从缓存重新加载 ViewModel 时,我需要能够拦截框架并执行重新初始化。由于没有重新创建 ViewModel,我既不能使用 Init()、MvxViewModel.InitFromBundle 也不能使用 MvxViewModel.ReloadFromBundle 方法。

我正在尝试调试单击后退按钮会恢复状态不一致的 ViewModel 的情况。某种 MvxViewModel.OnReloading() 会有所帮助。

有没有办法在 v3 中做到这一点?

编辑:

假设我有 FirstPageViewModel,它公开了一个导航到 SecondPageViewModel 的命令。根据我的观察,如果您在 SecondPageView 上单击模拟器的后退按钮,则不会构造 FirstPageViewModel。相反,我相信它是从某个缓存中检索的,然后绑定到视图。此缓存可能是 IMvxSingleViewModel 缓存的实现。

因此,在 ViewModel 构建之后调用 Init()、InitFromBundle() 和 ReloadFromBundle() 的常规流程不适用于此场景。换句话说,我需要一种重新初始化 ViewModel 的方法,无论它是刚刚构建的,还是从缓存中复活的。如果是前者,我可以使用 Init() 方法。如果后者为真,则无法在 ViewModel 本身内执行此操作。

这就是问题所在:

我有一个从 FirstViewModel 传递到 SecondViewModel 的 ICollectionService 实例。 FirstView 还包含一个绑定到此 CollectionService 的 ListView。因为 CollectionService 不是强类型的,所以我可以传递它并使用适当的项目模板在视图中呈现它的项目。

在显示 SecondViewModel 之前,FirstViewModel 会检索一些远程数据并填充 CollectionService。当显示 SecondViewModel 时,它的视图使用不同的项目模板显示来自 CollectionService 的数据。但是,如果我向后导航,由于 FirstViewModel 仍在引用 CollectionService,FirstView 将呈现 SecondViewModel 使用的数据,除非可以重新初始化 FirstViewModel,在此过程中清除 CollectionService。也许方法是错误的,但这是我问题的症结所在。

我不知道平台是否会有所不同,因为我希望在 Windows Phone 和 iOS 上会出现相同的行为,因为重新初始化将在核心模块中发生。尽管如此,这些都是对 Android 的观察。

TIA。

【问题讨论】:

  • 我完全不清楚你在问什么,什么是“缓存”,你的状态有什么不一致,你在什么平台上或者你想做什么。一个示例用户流程和/或一些示例问题代码可能会有所帮助。
  • 斯图尔特,感谢您的回复。我刚刚用其他信息编辑了我的问题。

标签: viewmodel mvvmcross viewmodellocator


【解决方案1】:

感谢您更新您的问题以提供更多信息。

使用 MvvmCross 方法进行跨平台开发使您能够利用原生 UI 平台。这确实意味着您 - 开发人员 - 确实需要了解一些有关这些平台的知识 - 要了解的关键内容之一是“视图”生命周期,包括在导航堆栈中使用时。

默认情况下,MvvmCross 不会缓存视图模型很长时间。在屏幕转换(例如旋转)期间偶尔会出现短期缓存,但不再存在长期缓存。相反,当创建一个新视图时,mvx 默认会创建一个新的视图模型来配合它——并且这对“终生”在一起——生命的结束由视图决定。

我建议您花一些时间阅读每个平台上的基本生命周期和导航范例。

  • 在 iOS 上,这意味着尤其要了解 UiNavigationController,它在内存中维护一个 UiViewController 堆栈(在 mvx 中,每个都将与单个视图模型结合)

  • WindowsPhone 中的情况类似,RootFrame 在 RAM 中维护一叠页面。这里有一个复杂的地方 - 墓碑 - 但在你确定基本生命周期之前忘记它。

  • 在 Android 上,情况类似,但略有不同。对于基本应用程序,您可能可以假设 Android 将在您的应用程序的生命周期中在 RAM 中维护一堆 Activity 页面。然而,Android 实际上远比这复杂得多——当操作系统决定回收内存时,它可以从 RAM 中删除 backstack 项。在这些情况下,有时会担心一些“墓碑”和脱水 - 但是,我再次建议您在掌握基本知识之前忽略这一点。

  • 在 winrt 上,默认情况下,情况实际上是您在描述中所理解的 - 后台堆栈只保存状态信息 - 视图本身不会缓存在 RAM 中。

以上故事希望能让您对每个平台上导航堆栈中的视图生命周期有所了解,从而也为您提供视图模型生命周期。


现在,如果您希望您的视图模型(或其他一些应用程序级别的对象)了解视图可见性状态,那么您需要拦截每个平台上的一些视图事件并将这些事件传递给到视图模型。

例如,您的 FirstViewModel 可以将 OnMadeVisible() 公开为自定义 Api。在这种情况下,您可以确保从 Windows 上的 OnNavigatedTo、Android 上的 OnResume 和 iOS 上的 ViewDidAppear 调用它


或者,如果您正在查看 ViewModel-ViewModel 通信的一般机制,那么我建议您查看类似


注意:

显然,导航堆栈不是唯一的导航范例 - 如果您的应用还使用弹出窗口、选项卡、拆分视图、汉堡包等,那么您也需要了解这些视图生命周期。

如果您对 View 生命周期有疑问,那么向其构造函数和关键生命周期事件添加跟踪是一个很好的第一步,


最后一点,如果您决定默认视图模型位置和视图模型生命周期不是您的应用所需要的 - 例如,如果您想使用单例视图模型,那么这很容易实现 - 请查看在您的应用程序中覆盖视图模型定位器App.cs 类。

【讨论】:

  • Stuart,感谢您的详细回复。我正在努力学习,所以这是非常有用的东西。我已经阅读了为我的 Xamarin 提供的 Andriod Activity 生命周期以获得基本的理解,但我想还有更多的故事。 Andriod 似乎重新创建了已导航离开的活动,因为每次调用 OnCreate 方法,即使没有重新创建相应的 ViewModel。将继续阅读......谢谢。
  • “每次都调用 OnCreate 方法,即使没有重新创建相应的 ViewModel”——这不是应该发生的事情。讨论它没有意义。如果您认为存在错误,请在某处发布可重现的案例并提出问题。
  • 好的,会调查的,谢谢。一个相关的问题。处置 ViewModel 时推荐的清理模式是什么?我应该重写 SaveStateToBundle 还是实现 IDisposable?否则,MvxViewModel 提供一个像 OnTerminating 之类的虚拟方法会不会是个好主意?
  • 没关系,我刚刚看到一些关于 ViewModel 关闭 stackoverflow.com/questions/14344576/… 的相关帖子
【解决方案2】:

即使知道这个问题已经 3 年了,我不确定在当前版本中是否有办法做到这一点,但我自己做了。 在此示例中,我将创建一个静态类,该类包含应用程序中 ViewModel 的所有实例。它以 null 静态变量开始,并在每个 ViewModel 实例化时获取每个值(在构造函数方法上)。

public static class ViewStackService
{
    //Stack
    private static exmp1ViewModel exmp1 = null;
    private static exmp2ViewModel exmp2 = null;
    private static exmp3ViewModel exmp3 = null;

    public static void addStackLevel(exmp1ViewModel _parent)
    {
        exmp1 = _parent;
    }

    public static void addStackLevel(exmp2ViewModel _parent)
    {
        exmp2 = _parent;
    }

    public static void addStackLevel(exmp3ViewModel _parent)
    {
        exmp3 = _parent;
    }


    public static async void burnAll()
    {

        if (exmp3 != null)
        {
            exmp3.DoBackCommand();
            await Task.Delay(250);
            exmp3 = null;
        }
        if (exmp2 != null)
        {
            //the OnResume method can be implemented here
            exmp2.DoBackCommand();
            await Task.Delay(250);
            exmp2 = null;
        }
        if (exmp1 != null)
        {
            //the OnResume method can be implemented here
            exmp1.DoBackCommand();
            await Task.Delay(250);
            exmp1 = null;
        }
    }
}

这些用作变量的 ViewModel 在每个 ViewModel 的构造函数启动时接收实例:

public class exmp1ViewModel 
    : MvxViewModel
{
    public exmp3ViewModel (){
        ViewStackService.addStackLevel (this);
    }
}

方法 burnAll() 将在调用时关闭所有 ViewModel。 这是有问题的,因为当我手动设置线程等待的时间时,它可能在某些不同的设备中存在错误,这取决于它的性能。但是使用这个类,你可以做一些其他的事情,比如检查一个 ViewModel 之前是否已经实例化来实例化一个新的,或者使用这个类来实现一个 OnResume 方法,以便在再次显示 ViewModel 时调用。 请记住,实例只能在未暂停时使用,也就是说,您只能在应用程序使用 ViewModel 时调用它的方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-12
    • 2018-06-16
    • 2014-10-12
    • 2016-04-12
    相关资源
    最近更新 更多