MvRx with Dagger2

Łukasz Chromy
Miquido News
Published in
5 min readJul 9, 2019

MvRx (pronounced “mavericks”) — So-called Android on Autopilot is a framework library from Airbnb designed to ease a few all-too-familiar everyday Android development pains like:

  • Configuration changes
  • Process restorations
  • Async calls
  • Initial state from fragment or activity arguments

MvRx provides a framework that makes Android screens, from the simplest to the most complex, easier to write than before.

If you are not familiar with this project, go ahead and take a look:

On the other hand, we have Dagger 2 — the most popular DI tool for our platform. It’s very commonly used in most of the projects in my company, Miquido.
When I played with MvRx for the first time, I wasn’t expecting any major problems between these two… But it wasn’t a piece of cake either.

When you type: MvRx + dagger in Google, you will eventually find this issue on GitHub:

There is a lot of knowledge in this conversation, but not everyone has time to read all these comments, code snippets, and smart ideas, just to setup MvRx with Dagger2. So I have decided to create a sample project to present how easy and fast it can be done.

Before we jump to the code, let’s talk a little bit about other tools we will use for this purpose.

Dagger 2 Android Injector

With Dagger 2.10 we got major API changes related to how dependencies are injected in Android.
Before these changes, to inject dependencies into an activity we would do something like this:

As we can see, our activity knows quite a lot about how it is initialized with its dependencies, which violates the principle of Inversion of Control:

The client delegates the responsibility of providing its dependencies to external code (the injector). The client code does not need to know about the injecting code.

After we take advantage of the new dagger.android, our activity will look a lot cleaner:

To read more about Dagger 2 Android Injector please visit the official docs at https://dagger.dev/android.

Assisted Injection

In MvRx we will need to manually inject dependencies from outside of our graph to the ViewModel. Assisted Inject comes to our aid for this. To learn more details about how it works please read an article by Fred Porciúncula: Dagger 2 on Android: Assisted Injection.

Let’s code!

If you want, you can check the code right away:

It’s a simple app with two screens: a list of users (@UsersFragment) and the user’s profile (UserProfileFragment). Both screens fetch data from web service using Retrofit. UserProfileFragment also retrieves data passed in Bundle to create its initial state.

State & ViewModel

Let’s start from creating our simple state containing only one Async property:

Read more about Async here.

Now we need a view model:

Let me explain it step by step:

  • UserViewModel constructor must be annotated with @AssistedInject.
  • UserState is not a dependency from our graph so it must be annotated with @Assisted.
  • UserService is a standard dependency from the graph — nothing special here.
  • fetchUser async call to fetch user data.
  • Factory interface with create method that receives all the parameters that are outside of our graph (annotated with @Assisted) and returns our UserViewModel. This interface also has to be annotated with @AssistedInject.Factory.

To take control of the creation of our ViewModel we need a companion object that implements MvRxViewModelFactory and overrides its create method. This method will be invoked anytime your ViewModel needs to be created.

Now it’s time for our fragment implementation.

Fragment

This is a fairly simple fragment which invokes our ViewModel’s fetchUser() method and then displays the results:

To create a UserViewModel we need to inject UserViewModel.Factory into this fragment. Let’s look again at our companion object in UserViewModel:

Its create method receives two arguments, let’s focus on the first one:

ViewModelContext contains a reference to the activity that hosts the fragment, and a reference to the fragment if the view model was created with a fragment scope. You should use the activity/fragment, if needed, to get or create your dagger/DI component and inject what you need. DO NOT pass a reference of your activity to your ViewModel. It will leak your Activity.

So now we can access UserViewModel.Factory by casting ViewModelContext to FragmentViewModelContext and then retrieve our fragment with a convenient method fragment<UserFragment>().
In this case, we are not doing anything with our state, so let’s simply pass it to the factory.

Initial state from fragment’s arguments

Our next screen requires one argument to be passed and set as our initial state. Take a look at UserProfileFragment:

The interesting part here is how we set the arguments during the creation of our fragment. Instead of using a custom key for our property, we use MvRx.KEY_ARG. MvRx will create the initial state for your ViewModel automatically. This will use the default constructor if possible. However, if you need to pass some default state such as id, you can create a secondary constructor that takes a parcelable args class. MvRx will automatically look for the key MvRx.KEY_ARG in the arguments in the fragment that created the ViewModel and call the secondary constructor of the state class.

Our UserProfileArgs contains only one param — userId, we use this id to get details of a given user in ViewModel:

That’s it! We managed to successfully inject our dependencies into our MvRx’s ViewModel.

MvRx is a simple but powerful framework. Try it yourself and I guarantee you will have a good time with it. I hope that thanks to this article you will save some time trying to combine it with Dagger2.

Please 👏🏻 *clap* 👏🏻 your hands if you enjoyed reading this post. It encourages me to keep writing and helps other people to find it:)

Thanks for reading!

--

--