MvvmCross ViewModel缓存和重新初始化

时间:2013-07-25 12:05:26

标签: viewmodel mvvmcross viewmodellocator

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

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

有没有办法在v3中执行此操作?

修改

假设我有FirstPageViewModel,它公开了一个导航到SecondPageViewModel的命令。根据我观察的内容,如果在SecondPageView上单击模拟器的后退按钮,则不会构造FirstPageViewModel。相反,我相信它会从某个缓存中检索,然后绑定到View。此缓存可能是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模块中发生。尽管如此,这些都是对Android的观察。

TIA。

2 个答案:

答案 0 :(得分:5)

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

使用MvvmCross方法进行跨平台开发使您可以利用本机UI平台。这确实意味着您 - 开发人员 - 确实需要了解这些平台 - 并且要理解的关键事项之一是“视图”生命周期,包括在导航堆栈中使用时。

默认情况下,MvvmCross不会在任何相当长的时间内缓存视图模型。在屏幕转换(例如旋转)期间偶尔会出现短暂的缓存,但不存在长期缓存。相反,当创建一个新视图时,默认情况下mvx会创建一个新的视图模型来与它一起使用 - 并且该对与“生命”保持在一起 - 生命的结束由视图决定。

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

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

  • WindowsPhone中的情况类似于RootFrame在RAM中维护一堆页面。这里有一个复杂的问题 - 墓碑 - 但是在你获得基本生命周期之后才会忘记它。

  • 在Android上,情况类似,但略有不同。对于基本应用程序,您可以假设Android将在应用程序的生命周期内在RAM中维护一堆活动页面。然而,Android实际上要复杂得多 - 操作系统可以在决定回收内存时从RAM中删除背堆物品。在这些情况下,有时候会出现一些“墓碑”和脱水现象 - 但是,我再次建议你忽略这一点,直到你掌握了基础知识。

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

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


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

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


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

的内容

注意:

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

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


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

答案 1 :(得分:0)

即使知道这个问题是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是否在实例化新实例之前是否已实例化,或者在再次显示ViewModel时使用该类实现要调用的OnResume方法。 请记住,实例只能在未暂停时使用,也就是说,只有在应用程序使用ViewModel时才能调用它。