Mvvm跨视图模型缓存和重新初始化



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

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

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

编辑:

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

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

这就是问题所在:

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

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

我不知道这个平台是否有什么不同,因为我预计在Windows Phone和iOS上会有同样的行为,因为这个重新初始化将在Core模块中发生。尽管如此,这些都是对安卓系统的观察。

TIA。

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

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

默认情况下,MvvmCross不会缓存视图模型任何显著的时间长度。在屏幕转换(例如旋转)期间偶尔会出现短暂的缓存,但不再有长期缓存。相反,当创建了一个新视图时,mvx默认情况下会创建一个新的视图模型,这对视图将"终身"保持在一起,生命的结束由视图决定。

我鼓励您花点时间阅读每个平台上的基本生命周期和导航范式。

  • 在iOS上,这意味着特别要学习UiNavigationController,它在内存中维护一堆UiViewController(在mvx中,每一个都将与一个单独的视图模型相结合)

  • 在WindowsPhone中的情况类似,RootFrame在RAM中维护一堆页面。这里有一个复杂的问题——墓碑——但在你确定了基本的生命周期之前,请忘记这一点。

  • 在安卓系统上,情况类似,但略有不同。对于基本应用程序,你可能会假设Android在应用程序的使用寿命内会在RAM中维护一堆"活动"页面。然而,Android实际上远比这复杂得多——当操作系统决定回收内存时,它可以从RAM中删除后台项目。在这些情况下,有时会担心一些"墓碑"和脱水——但是,我再次建议你忽略这一点,直到你掌握了基本知识。

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

上面的故事有望让您了解每个平台上导航堆栈中的视图生命周期,因此也为您提供了视图模型生命周期。


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

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


或者,如果你正在研究ViewModel ViewModel通信的通用机制,那么我建议你研究

  • 中CollectABull教程中使用的信使http://mvvmcross.wordpress.com
  • Greg的视图模型结果模式http://gregshackles.com

注:

显然,导航堆栈并不是唯一的导航模式——如果你的应用程序也使用弹出按钮、选项卡、拆分视图、汉堡包等,那么你也需要了解这些视图的生命周期。

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


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

即使知道这个问题已经存在3年了,我也不确定在当前版本中是否有办法做到这一点,但我自己做了。在本例中,我将创建一个静态类,该类包含应用程序中ViewModels的所有实例。它从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之前是否已经实例化以实例化新的ViewModel,或者使用该类实现在ViewModel再次显示时调用的OnResume方法。请记住,实例只有在未暂停时才能使用,也就是说,您只能在应用程序使用ViewModel时调用它的方法。

最新更新