Android activity life cycle and IoC

I was recently working on an Android (Xamarin, .net) application based on MvvmCross framework. Actually not just an Android app since it could be ported quite easily to other platforms, such as Windows Phone 8 or IPhone. Anyway, I was using inversion of control principle in a slightly incorrect way and thus done a mistake which revealed only a while after the deployment.

Here are the symptoms: Application would cold start fine, would resume fine when resume was done within reasonable time (no idea, depends on the device, in my case could be hours and it would work perfectly) but it wouldn’t resume well if the timespan was too much (i.e. a day or several hours). Instead of displaying the activity content it was just an actionbar with title without any content of menuitems.

By looking at android’s log on the device itself it was clear that there were problems with IoC resolve method. It yielded an “Object reference null” type of exception. I was resolving the reference within activity’s constructor. Odd error, since MvvmCross is supposed to trigger IoC registration  before the first activity is run. But somehow it wasn’t. I’ve mentioned that to Stuart (@slodge, man behind MvvmCross) and he instantly pointed to the mistake I made – I neglected the Android activity’s lifecycle. I immediately understood my mistake: I am used to put IoC references within constructors as arguments, which is mostly fine. Except when it comes to Android activities. The thing is that the entry point of a suspended application (with activity destroyed) is the activity itself, more precisely, its constructor. MvvmCross does trigger the IoC registration method correcly, but, of course, only after the activity is created and hence I was having errors in this particular situation.

The solution is fairly simple – move IoC resolving within OnCreate method, not sooner – that’s the point where you can be certain that MvvmCross initialization is done.

Before:

public abstract class SomeClass<T> : Activity
{
    protected readonly IFragmentPresenter fragmentPresenter;

    public MvxActivityFragmentHost(): this(Mvx.Resolve<IFragmentPresenter>())
    {}

    public MvxActivityFragmentHost(IFragmentPresenter fragmentPresenter)
    {
        this.fragmentPresenter= fragmentPresenter;
    }
    ...
}

after

public abstract class SomeClass<T> : Activity
{
    protected IFragmentPresenter fragmentPresenter;

    protected override void OnCreate (Bundle bundle)
    {
        base.OnCreate (bundle);
        fragmentPresenter = Mvx.Resolve<IFragmentPresenter>();
    }
    ...
}

This change did the trick. A bit more work to inject the mock reference when testing but not a big deal.

Now, if you followed the post you might be asking, why the heck does application cold starts just fine, since it should get the same error. The explanation is rather simple – I am using a splashscreen activity when application cold starts. This one doesn’t have any IoC resolving involved at all, but it does initialize MvvmCross. So when it gets to the my problematic activity, the IoC is already in place.

Now, there you have it. Respect the Android activity lifecycle otherwise it will bit you.

Leave a Reply