Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MvvmCross ViewModel caching and re-initializing

I need to be able to intercept the framework and perform so re-initialization when a ViewModel is being reloaded from the cache. Since the ViewModel is not being recreated, I can neither use the Init(), MvxViewModel.InitFromBundle, nor MvxViewModel.ReloadFromBundle methods.

I am trying to debug a situation where clicking on the back button restores a ViewModel with inconsistent state. Some sort of MvxViewModel.OnReloading() would help.

Is there a way to do this in v3?

EDIT:

Assume I have FirstPageViewModel which exposes a command to navigate to SecondPageViewModel. Based on what I am observing, if you click on the back button of the simulator while on the SecondPageView, FirstPageViewModel is not constructed. Instead, it is retrieved, I believe, from some cache, then bound to the View. This cache is possibly an implementation of IMvxSingleViewModel cache.

Thus, the regular flow after ViewModel construction, where you call Init(), InitFromBundle() and ReloadFromBundle() does not apply in this scenario. In other words, I need a way to re-initialize a ViewModel regardless of whether it was just newly constructed, or it being resurrected from a cache. If the former, I can use an Init() method. If the latter is true, there is no way to do this within the ViewModel itself.

This is the problem:

I have an instance of ICollectionService which is passed from FirstViewModel to SecondViewModel. FirstView also contains a ListView that is bound to this CollectionService. Because CollectionService is not strongly typed I can pass it around and use the appropriate item template to render its items in the view.

Before showing SecondViewModel, FirstViewModel retrieves some remote data and populates the CollectionService. When SecondViewModel is shown, its view displays data from the CollectionService using a different item template. However, if I navigate back, since FirstViewModel is still referencing the CollectionService, FirstView will render data that was used by SecondViewModel unless FirstViewModel could be re-initialized, clearing the CollectionService in the process. Maybe the approach is wrong but this is the crux of my problem.

I do not know if the platform makes a difference, as I would expect the same behavior on Windows Phone and iOS as this re-initialization will occur in the Core module. Nonetheless these are observations on Android.

TIA.

like image 649
Klaus Nji Avatar asked Jul 25 '13 12:07

Klaus Nji


1 Answers

Thanks for updating your question to provide much more information.

Using the MvvmCross approach to cross-platform development enables you to leverage native UI platforms. This does mean that you - the developer - do need to learn a bit about these platforms - and one of the key things to understand is the "view" lifecycle, including when used in navigation stacks.

By default MvvmCross does not cache view models for any significant length of time. There are occasional short-lived caches which occur during screen transitions (eg rotation) but there are no longer term caches. Instead, when a new view is created then mvx by default creates a new viewmodel to go with it - and that pair stay together "for life" - with the end of life being determined by the view.

I'd encourage you to take some time to read about the basic lifecycle and navigation paradigms on each platform.

  • on iOS this means especially learning about UiNavigationController which maintains a stack of UiViewControllers in memory (each of which will in mvx be married to an individual viewmodel)

  • the situation is similar in WindowsPhone with the RootFrame maintaining in RAM a stack of pages. There is a complication here - tombstoning - but forget about that until after you get the basic lifecycle nailed.

  • on Android, the situation is similar, but slightly different. For basic apps you can probably assume that Android will maintain a stack of Activity pages in RAM during your app's lifetime. However, Android is actually far more complex than that - the OS can remove backstack items from RAM when it decides to reclaim memory. In these cases there's some 'tombstoning' and dehydration to sometimes worry about - but, again I'd recommend you ignore that until after you have the basics under the belt.

  • on winrt, by default the situation is actually what you understood in your description - the backstack only holds state information - the views themselves aren't cached in RAM.

The above stories hopefully give you some idea of view lifecycle in navigation stacks on each platform - and hence also give you the viewmodel lifecycle too.


Now, if you are in the situation that you want your viewmodels (or some other app level objects) to know about the view visibility state, then you'll need to intercept some view events on each platform and pass these events over to the view models.

For example, your FirstViewModel could expose OnMadeVisible() as a custom Api. In that case, you could ensure this were called from OnNavigatedTo on Windows, OnResume on Android and ViewDidAppear on iOS


Alternatively if you are looking at general mechanisms for ViewModel-ViewModel communication then I would recommend you look at something like

  • the messenger use in the CollectABull tutorial in http://mvvmcross.wordpress.com
  • Greg's viewmodel result pattern on http://gregshackles.com

Note:

Obviously, navigation stacks are not the only navigation paradigm - if your app also uses flyouts, tabs, splitviews, hamburgers, etc then you'll need to understand those view lifecycles too.

If ever you are in doubt about View lifecycles, then adding trace to their constructors and key lifecycle events is a good first step,


As a final note, if you decide the default viewmodel location and viewmodel lifecycle is not what your app needs - eg if you want to use singleton viewmodels, then this can be easily achieved - look at overriding the view model locator in your App.cs class.

like image 139
Stuart Avatar answered Nov 03 '22 00:11

Stuart