MVVMCross ViewModel construction failure notificat

2019-09-07 19:39发布

问题:

We've noticed a couple of times in our mobile applications that users have reported the application hanging or seeming to become unresponsive between views / rare crashes when switching between views. We've tracked down these cases to when our view model constructors throw uncaught exceptions.

We want to put a solution in place so that if a view model fails to construct for some reason then we can notify the user and provide some message that will be useful to us when it's logged through support.

I've been taking a look at doing this but haven't found a reliable way to achieve this.

The first thing we tried was at the IMvxViewModelLocator level. We already have a custom implementation of IMvxViewModelLocator so we've modified this. We allow all exceptions to be thrown and then we have an IErrorHandler interface which each platform implements. We then call this to attempt to show a dialog. This has proved to be unreliable and the dialog does not always display. Something along the lines of: (note - here ResolveViewModel will always return true or throw)

    public override bool TryLoad(Type viewModelType, IMvxBundle parameterValues, IMvxBundle savedState, out IMvxViewModel viewModel)
    {
        try
        {
            return ResolveViewModel(viewModelType, parameterValues, savedState, out viewModel);
        }
        catch (Exception exception)
        {
            _errorHandler.HandleViewModelConstructionException(viewModelType, exception);
            viewModel = null;
            return false;
        }
    }

What we would ideally like to do is intercept any failure to construct a view model and then re-request an ErrorViewModel. We've tried to do this 2 ways:

1) We've tried defining a custom IMvxViewDispatcher for each platform and we're trying to intercept failures as below but if an exception in the constructor is thrown we never get back this far:

public class TouchDispatcher : MvxTouchUIThreadDispatcher, IMvxViewDispatcher
{
    private readonly IMvxTouchViewPresenter _presenter;

    public TouchDispatcher(IMvxTouchViewPresenter presenter)
    {
        _presenter = presenter;
    }

    public bool ShowViewModel(MvxViewModelRequest request)
    {
        Action action = () =>
            {
                _presenter.Show(request);
            };
        try
        {
            bool success = RequestMainThreadAction(action);
            return !success ? HandleError() : success;
        }
        catch (Exception)
        {
            return HandleError();
        }
    }
    // Other bits
}

2) We thought we might have more success at the presenter level. We modified our ViewPresenter for each platform and we have overridden void Show(MvxViewModelRequest request). This has not proved to be successful either as exceptions don't propagate back this far.

This leaves me thinking that maybe we are better attempting this at the IMvxViewModelLocator level again.

Has anyone found a way to reliably intercept failures to construct view models and then ideally re-request a different view model / present some dialog to the user?

回答1:

It seems you've identified that the core of the problem is when: "view model constructors throw uncaught exceptions."

This is going to be slightly problematic as the ViewModel's are generally constructed during View lifecycle overrides like ViewDidLoad, OnCreate or NavigatedTo - which is generally after the Presenter has finished requesting presentation.

As you've already found an easy place to identify when ViewModel construction has failed is in a custom IMvxViewModelLocator - others likeIMvxViewModelLoader are also possible. This is probably the easiest place to catch the error and to trigger the error handling - you can then get hold of the IMvxViewDispatcher (or presenter) there in order to change the display. However, you will still need to make sure your Views can handle null created ViewModels - as the ViewDidLoad, etc calls will still need to complete.



标签: mvvmcross