Search code examples
android-fragmentsxamarin.androidmvvmcross

MvvmCross Android - Null Reference for ViewModel when Reloading Fragments


Source code - https://github.com/benhysell/V.FlyoutTest/

Issue - While intermixing fragments and base views and using the settings on the device - "Developer Options / Don't keep Activities" ViewModels are null if OnDestory() is called and then the activity is brought back into view. This results in a null reference exception and app crash.

Description I have a slideout/hamburger menu in Android using MvvmCross. On application start the following steps take place:

  • HomeViewModel is set to load
  • HomeViewModel loads HomeView setting up the slideout/hamburger menu
  • Since the HomeView is never really shown, just manages the fragments it calls and shows EnterTimeViewModel, the default view, and the one we really care about.
  • At the end of loading HomeView a message is generated using the messenger plugin to "Log In" to the system
  • Message is received in the EnterTimeViewModel, normally here we'd do some sort of checking to see if we are logged in or not, but in this case we just call ShowViewModel with LoginViewModel to simulate a log in request and prompt for the user.

  • LoginViewModel is not a fragement, just a normal base view. It is shown to the user

  • While that is happening, HomeView is being removed from the system because we have set "Developer Options / Don't keep Activities"
  • User logs into the system by hitting the log in, in demo this is a no-op and it just closes the LoginView
  • The HomeView is then restored, except during that restoration the EnterTimeViewModel is null...when the EnterTime view attempts to access the EnterTimeViewModel a null reference is hit.

The sequence of events with regards to MvvmCross transitions is as follows while loading and unloading HomeView:

  • OnCreate
  • StartActivityForResult
  • OnStart
  • OnResume
  • OnPause
  • OnSaveInstanceState
  • OnStop
  • OnDestroy

Login Screen is shown, after login

  • OnCreate
  • OnStart
  • Crash, null reference exception

It feels like a few steps are skipped, resulting in the crash. Ideas? Places to go looking?

As a point of reference, the project I based my sliding menu on, https://github.com/jamesmontemagno/Xam.NavDrawer exhibits the same issue.

Update Implemented a fix http://benjaminhysell.com/archive/2014/06/mvvmcross-flyoutnavigation-hamburger-menu-sliding-menu-for-android-null-reference-exception-on-fragment-shown-fix/, tldr - took responsibility to try and save the ViewModels, and worst case re-create them if the fragment is unloaded from memory.


Solution

  • From your code, it looks like you are manually setting the ViewModel's of your fragments when you first create them:

                frag.ViewModel = viewModelLocal;
    

    from https://github.com/benhysell/V.FlyoutTest/blob/master/V.FlyoutTest.Droid/Views/HomeView.cs#L153

    When your hosting Activity is torn down because of "Don't keep Activities" and then shown again, then Android will save the fragment state in the instanceState bundle, and it will try to recreate those fragments - which (I guess) is when you are seeing your NullReferenceException.

    To work around this you will need either:

    • to find some way to recreate your ViewModel during OnCreateView when the bundle is present. You could do this using serialisation/deserialisation techniques or by moving the ViewModel=viewModelLocal code inside your OnCreateView method instead of inside the owning Activity
    • to block the default Android fragment recreation (e.g. by stopping the parent bundle from using its OnCreate bundle and using your own reinflation logic)

    Note: this behaviour doesn't happen for Activity as MvvmCross can reuse the Intent there in order to recreate the ViewModel for the Activity. But for Fragments the app has taken responsibility for creating these (in its custom presenter), so currently the app also has to take responsibility to recreate the ViewModel too.