Efficient and bug-free fragment injection in Android MVP applications

Maksym Plencler
inspace labs
Published in
4 min readApr 11, 2016

In this article, we introduce a simple extension for base fragment interface that we use in Android applications with MVP architecture. In order to take advantage of Dependency Injection pattern we have to be aware of injected view’s lifecycle. We use that knowledge to write efficient and stable applications. The simple solution that we propose will protect your application from unwanted bugs without compromising its performance. After reading the article you will familiar with an interesting use case for testing your application.

Introduction

We emphasize on the quality of our solutions when crafting applications. That’s why we continue to improve the tools and guidelines that we use.

It has always been struggle for Android developers to find architecture for project that make the application easy to maintain and to test. Recently a concept of Model-View-Presenter (MVP) architecture has become very popular. You can find many sources that refers this architecture concept. We especially recommend the work of Fernando Cejas, which became an inspiration for us, and to which we refer often.

One definition says that efficiency is about doing things in an optimal way. By using MVP architecture we are giving up some efficiency as we are putting extra effort to stick to the MVP guidelines. But we consider it an investment, and it pays off quickly. Dependency Injection (DI) is technology with many advantages and one of them is reducing the extra effort, so with DI, applying MVP to your project is not that expensive.

The story

Basically, every fragment in our application extends BaseFragment as it provides method for getting DI component. The component is responsible for injecting fields annotated with @Inject annotation.

As you can see fragment’s parent activity that implements HasComponent interface is an owner of the component. All we need to know is that the component is being initialized on activity’s Activity.onCreate(Bundle) callback.

Have a look on a gist of SampleFragment that extends BaseFragment.

The field injection is performed on onActivityCreated(Bundle). This lifecycle callback tells the fragment that its activity has completed its own Activity.onCreate(). This sounds like good fit for referencing to activity’s fields from fragment context, isn’t it?

But is it efficient? By design, activity is destroyed and recreated on configuration changes (such as a change in screen orientation, language, etc). This means that onActivityCreated(Bundle) can be called multiple times during lifecycle of an injected fragment. This leads to the loss of state of any field injected previously. That’s bad if the state was result of any expensive operation (fetching data from server, etc). OK, we can fix that:

Referencing DI component on onCreate(Bundle) is possible as we are creating and attaching fragment instances on Activity.onCreate(Bundle). This is the easiest way to guarantee single fragment injection as Fragment.onCreate(Bundle) won’t be called on orientation changes.

Bug hunting

The problem is when the application stays in the background for a while and a recently displayed activity is recycled by Android. Bringing back the application causes NullPointerException (NPE) as the activity’s component is called by the fragment before it is initialized (both fragment and activity are recreated at the same time).

At first, the only way to reproduce this bug was to keep activity in background for a long time, or start multiple applications simultaneously hoping Android will destroy our application…

Developers tips and tricks:
But there’s an easy way to reproduce such scenario. To reproduce: switch ON Don’t keep activities under Apps in Settings/Developer options. This will destroy every activity as soon as the user leaves it.

This way we were able to look into logs to find out what caused NPE.

Solution

The solution is very simple as I mentioned early in the article.

We’ve introduced two lifecycle methods in BaseFragment class.

  1. onInjectView() — called to do an optional injection on onCreate(Bundle) and if an exception is thrown or false returned, it is called on onActivityCreated(Bundle) again. Within this method you can get the injection component and inject the view. Retrun true if the injection was succesfull, then it will not be called again.
  2. Based on returned value, the second method is called. The method is named onViewInjected(Bundle), as it is called only when the fragment has been injected and injected fields can be initialized.

Usage is simple like that:

Conclusion

If you are using Dependency Injection in the similar way (and if you stick to MVP guidelines you probably do), the solution that we propose will protect your application from unwanted bugs without compromising its performance. I hope you will find this article useful.

References:

The Clean Architecture by Uncle Bob
Architecting Android…The clean way? by Fernando Cejas

--

--