Android MVP with Dagger2 – the Google way

So there have been a lot of ways to implement MVP in Android. Using Dagger2, without it, with things like BasePresenters or passing the presenter to the fragment instead of injecting it etc. I found THIS great link with examples how Google use MVP which can be helpful to some of you.

I want to describe how Google uses the Dagger2 implementation with MVP. I will try to keep it short.

Inject the views in the presenter

They are using Dagger2 to inject the current view and provide it in the presenter. Here is the code:

public class TaskDetailActivity extends AppCompatActivity {

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.taskdetail_act);

         ...
        // Create the presenter
        DaggerTaskDetailComponent.builder()
                .taskDetailPresenterModule(new TaskDetailPresenterModule(taskDetailFragment, taskId))
                .tasksRepositoryComponent(((ToDoApplication) getApplication())
                .getTasksRepositoryComponent()).build()
                .inject(this);
    }

}

which is then injected in the presenter:

final class TaskDetailPresenter implements TaskDetailContract.Presenter {

    private TasksRepository mTasksRepository;

    private TaskDetailContract.View mTaskDetailView;

    /**
     * Dagger strictly enforces that arguments not marked with {@code @Nullable} are not injected
     * with {@code @Nullable} values.
     */
    @Nullable String mTaskId;
    /**
     * Dagger strictly enforces that arguments not marked with {@code @Nullable} are not injected
     * with {@code @Nullable} values.
     */
    @Inject
    TaskDetailPresenter(@Nullable String taskId,
            TasksRepository tasksRepository,
            TaskDetailContract.View taskDetailView) {
        mTasksRepository = tasksRepository;
        mTaskDetailView = taskDetailView;
        mTaskId = taskId;
    }

    @Inject
    void setupListeners() {
        mTaskDetailView.setPresenter(this);
    }
  ...
}

So the moment they create the presenter, they injected the view in it. And they passed the fragment as the view for this presenter. Looks like a good idea!

Initialize the presenter with method injection

To pass the presenter to the view they use method injection. As you can see in the “setupListeners” method, they use the @Inject annotation, which forces Dagger2 to execute it immediately after the constructor injection.

isActive method as part of the view

Every view which is connected with the presenter has the isActive method to check if the view is still there, visible and not garbage collected. This method in the case of a fragment is simple as this:

public class TaskDetailFragment extends Fragment implements TaskDetailContract.View {
    ...
    @Override
    public boolean isActive() {
        return isAdded();
    }
}

Presenter is passed to the fragments

So the presenter itself is created in the Activity but it is passed to the fragments with the method injection. Passing the presenter from the activity to the fragments is one of the ways you could do this. There are  several ways you can get the presenter from the fragments:

  • Pass it to them from the activity. Find them by tag and just have a method accepting a presenter in each of the fragments.
  • Pass the data only to the fragments if they accept the same data.
  • Cast the activity to a HasComponent interface and get the component from there. Then inject the current fragment. This is done in the Clean Architecture example.
    • You need the activity component so you won’t have to use your Application class to get the ApplicationComponent and inject fragments there. It is a way to keep your dependencies structured.

Passing multiple dependencies with the same interface from Dagger

If you want to use two TaskDataSource’s like a Local data source and a Remote data source. You want them to be both injectable despite that they have the same interface. Like THIS case. How do you do that?
You add a @Local and @Remote annotations that serve as a tag to Dagger to distinguish between the different types. Like HERE. Then before injecting you specify which of the types you want to be injected within your app with the annotation.

What is the bad thing

Well, in all of the activities Google inject the concrete implementation of the presenter as you can see HERE.

public class StatisticsActivity extends AppCompatActivity {

    private DrawerLayout mDrawerLayout;

    @Inject StatisticsPresenter mStatiticsPresenter;
    ...
}

They have a class called StatisticsContract.Presenter which is an interface of the presenter that you should work with, not with a concrete implementation. But nowhere in their modules they provide the concrete implementation. That is why Dagger2 cannot know who is the right presenter if you want to inject the interface. And to start the whole dependency injection going you have to init it with the injection of the concrete implementation. Or just add an @Provides method to the module which will return the concrete implementation of the presenter.

You may also like...