Android MVP and Dagger2

Filed Under: Android

In this tutorial, we’ll be implementing MVP pattern along with Dagger2 in our Android Application. It’s recommended to go through each of these design pattern concepts individually before using them together.

Android MVP and Dagger2

We know that MVP is based on the separation of concerns. MVP pattern separates our code into three layers – Model, View and Presenter.

Presenter acts as a bridge between the Model and the View. Model and View cannot communicate with each other WITHOUT the Presenter.

Presenter-Model, Presenter-View communicate using Interfaces.

Dagger2 is a Dependency Injection Library developed by Square. Instead of creating dependencies in our classes, we provide them by using the Dagger2 core structure – Module, Component, Inject and Provides.

Using the Dagger2 annotations, the compiler builds the Dagger classes automatically at compile-time.

MVP + Dagger2 = Beginning our Journey to write clean code that follows the SOLID Principles.

So in the following section, we’ll use Dagger2 in our MVP Android Studio Project from this tutorial. This would give us a clarity as to how Dagger2 works with MVP.

Android MVP Dagger2 Project Structure

android mvp dagger2 project structure

model – The Model class of the Project.

view – Our activity is the view here.

presenter – The presenter class, which ultimately tells the Activity what to do with the View.

MainContract.java – Consists of the interfaces for the Model, View and Presenter.

di – Consists of Dependencies Injection – Dagger2 classes.

Don’t forget to add the following dependencies in your build.gradle file.


annotationProcessor 'com.google.dagger:dagger-compiler:2.13'
implementation 'com.google.dagger:dagger:2.13'

Android MVP Dagger2 Code

Let’s look at the interfaces defined for the MVP classes in our MainContract.java class.


package com.journaldev.mvpdagger2;
public interface MainContract {

    interface ViewCallBack {
        void showProgress();

        void hideProgress();

        void setQuote(String string);
    }

    interface ModelCallBack {
        interface OnFinishedListener {
            void onFinished(String string);
        }

        void getNextQuote(OnFinishedListener onFinishedListener);
    }

    interface PresenterCallBack {
        void onButtonClick();

        void onDestroy();
    }
}

So the View has interfaces for setting a string, showing or hiding a ProgressBar.

The Model has an interface onFinished which triggers after the handler interval succeeds.

The Presenter controls the activity button click and onDestroy() lifecycle.

Model.java


package com.journaldev.mvpdagger2.model;

import android.os.Handler;

import com.journaldev.mvpdagger2.MainContract;

import java.util.Arrays;
import java.util.List;
import java.util.Random;

public class Model implements MainContract.ModelCallBack {

    @Override
    public void getNextQuote(final MainContract.ModelCallBack.OnFinishedListener listener) {
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                listener.onFinished(getRandomQuote());
            }
        }, 1200);
    }

    private List<String> arrayList = Arrays.asList(
            "Be yourself. everyone else is already taken.",
            "A room without books is like a body without a soul.",
            "You only live once, but if you do it right, once is enough.",
            "Be the change that you wish to see in the world.",
            "If you tell the truth, you don't have to remember anything."
    );

    private String getRandomQuote() {

        Random random = new Random();
        int index = random.nextInt(arrayList.size());
        return arrayList.get(index);
    }
}

The presenter implementation is defined in the MainPresenterImpl.java class.


package com.journaldev.mvpdagger2.presenter;

import com.journaldev.mvpdagger2.MainContract;
import com.journaldev.mvpdagger2.model.Model;


public class MainPresenterImpl implements MainContract.PresenterCallBack, MainContract.ModelCallBack.OnFinishedListener {

    private MainContract.ViewCallBack mainView;
    private Model model;


    public MainPresenterImpl(MainContract.ViewCallBack mainView, Model model) {
        this.mainView = mainView;
        this.model = model;
    }

    @Override
    public void onButtonClick() {
        if (mainView != null) {
            mainView.showProgress();
        }
        model.getNextQuote(this);
    }

    @Override
    public void onDestroy() {
        mainView = null;
    }

    @Override
    public void onFinished(String string) {
        if (mainView != null) {
            mainView.setQuote(string);
            mainView.hideProgress();
        }
    }
}

di package

In the di package we define the dependency injection modules and components.

Let’s look at the Modules first.

AppModule.java


package com.journaldev.mvpdagger2.di.module;

import android.app.Application;

import com.journaldev.mvpdagger2.InitApplication;

import javax.inject.Singleton;

import dagger.Module;
import dagger.Provides;

@Module
public class AppModule {

    private InitApplication initApplication;

    public AppModule(InitApplication initApplication) {
        this.initApplication = initApplication;
    }

    @Provides
    @Singleton
    public Application provideApplication() {
        return initApplication;
    }
}

This provides an application instance across the project.

ContextModule.java


package com.journaldev.mvpdagger2.di.module;

import android.content.Context;
import dagger.Module;
import dagger.Provides;

@Module
public class ContextModule {
    private Context context;

    public ContextModule(Context context) {
        this.context = context;
    }

    @Provides
    public Context provideContext() {
        return context;
    }
}

This provides getApplicationContext()

DataModule.java


package com.journaldev.mvpdagger2.di.module;

import com.journaldev.mvpdagger2.model.Model;

import dagger.Module;
import dagger.Provides;

@Module
public class DataModule {

    @Provides
    public Model provideModelClass() {
        return new Model();
    }
}

This provides an instance of Model class for us.

MvpModule.java


package com.journaldev.mvpdagger2.di.module;

import com.journaldev.mvpdagger2.MainContract;
import com.journaldev.mvpdagger2.model.Model;
import com.journaldev.mvpdagger2.presenter.MainPresenterImpl;

import dagger.Module;
import dagger.Provides;

@Module
public class MvpModule {

    private MainContract.ViewCallBack viewCallBack;

    public MvpModule(MainContract.ViewCallBack viewCallBack) {
        this.viewCallBack = viewCallBack;
    }

    @Provides
    public MainContract.ViewCallBack provideView() {
        return viewCallBack;
    }

    @Provides
    public MainContract.PresenterCallBack providePresenter(MainContract.ViewCallBack view, Model model) {
        return new MainPresenterImpl(view, model);
    }
}

This provides an instance of the MainPresenterImpl.java class.

It takes the View’s interface and the Model class as it’s constructor arguments.

ActivityScope.java


package com.journaldev.mvpdagger2.di.scope;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import javax.inject.Scope;

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScope {
}

This sets the scope for the components which we’ll see next.

AppComponent.java


package com.journaldev.mvpdagger2.di.component;

import android.app.Application;
import android.content.Context;

import com.journaldev.mvpdagger2.InitApplication;
import com.journaldev.mvpdagger2.di.module.AppModule;
import com.journaldev.mvpdagger2.di.module.ContextModule;
import com.journaldev.mvpdagger2.di.module.DataModule;
import com.journaldev.mvpdagger2.model.Model;

import javax.inject.Singleton;

import dagger.Component;

@Singleton
@Component(modules = {AppModule.class, DataModule.class, ContextModule.class})
public interface AppComponent {
    void inject(InitApplication initApplication);

    Context getContext();

    Model getFindItemsInteractor();

    Application getApplication();
}

This is the App level Component. The fields would be accessible throughout the application.

ActivityComponent.java


package com.journaldev.mvpdagger2.di.component;

import com.journaldev.mvpdagger2.MainContract;
import com.journaldev.mvpdagger2.di.module.MvpModule;
import com.journaldev.mvpdagger2.di.scope.ActivityScope;
import com.journaldev.mvpdagger2.view.MainActivity;

import dagger.Component;

@ActivityScope
@Component(dependencies = AppComponent.class, modules = MvpModule.class)
public interface ActivityComponent {
    void inject(MainActivity mainActivity);

    MainContract.PresenterCallBack getMainPresenter();
}

This Component is used to inject the Presenter in our MainActivity.

The code for the InitApplication.java is given below:


package com.journaldev.mvpdagger2;

import android.app.Application;
import android.content.Context;
import com.journaldev.mvpdagger2.di.component.AppComponent;
import com.journaldev.mvpdagger2.di.component.DaggerAppComponent;
import com.journaldev.mvpdagger2.di.module.AppModule;
import com.journaldev.mvpdagger2.di.module.ContextModule;
import com.journaldev.mvpdagger2.di.module.DataModule;


public class InitApplication extends Application {

    private AppComponent component;

    public static InitApplication get(Context context) {
        return (InitApplication) context.getApplicationContext();
    }

    @Override
    public void onCreate() {
        super.onCreate();
        component = DaggerAppComponent.builder()
                .appModule(new AppModule(this))
                .contextModule(new ContextModule(this))
                .dataModule(new DataModule())
                .build();
    }

    public AppComponent component() {
        return component;
    }
}

The code for the activity_main.xml layout file is given below.


<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/textView"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:padding="8dp"
        android:text="Implementing MVP and Dagger2 in this Application."
        android:textAppearance="?android:attr/textAppearanceSearchResultTitle"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="@android:dimen/notification_large_icon_height"
        android:text="GET NEXT QUOTE"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView" />

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyleLarge"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="gone"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

The code for the MainActivity.java class is given below:


package com.journaldev.mvpdagger2.view;

import android.content.Context;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import com.journaldev.mvpdagger2.InitApplication;
import com.journaldev.mvpdagger2.MainContract;
import com.journaldev.mvpdagger2.R;
import com.journaldev.mvpdagger2.di.component.DaggerActivityComponent;
import com.journaldev.mvpdagger2.di.module.MvpModule;

import javax.inject.Inject;

import static android.view.View.GONE;


public class MainActivity extends AppCompatActivity implements MainContract.ViewCallBack {


    @Inject
    MainContract.PresenterCallBack presenterCallBack;

    @Inject
    Context mContext;

    private TextView textView;
    private ProgressBar progressBar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        DaggerActivityComponent.builder()
                .appComponent(InitApplication.get(this).component())
                .mvpModule(new MvpModule(this))
                .build()
                .inject(this);


        textView = findViewById(R.id.textView);
        Button button = findViewById(R.id.button);
        progressBar = findViewById(R.id.progressBar);


        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                presenterCallBack.onButtonClick();
            }
        });
    }

    @Override
    protected void onResume() {
        super.onResume();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        presenterCallBack.onDestroy();
    }

    @Override
    public void showProgress() {
        progressBar.setVisibility(View.VISIBLE);
        textView.setVisibility(View.INVISIBLE);
    }

    @Override
    public void hideProgress() {
        progressBar.setVisibility(GONE);
        textView.setVisibility(View.VISIBLE);
    }

    @Override
    public void setQuote(String string) {
        textView.setText(string);
        Toast.makeText(mContext, "Quote Updated", Toast.LENGTH_SHORT).show();
    }
}

Voila! We inject the Presenter in here without instantiating it. Thanks to Dagger2. Now compare this activity source code with the one here.

The output of the above application in action is given below.

android mvp dagger2 android app output

This brings an end to MVP and Dagger2 integration tutorial. You can download the AndroidMVPDagger2 Project from the link below.

Comments

  1. Vijay says:

    Nice Tutorial

  2. Bahaa says:

    you forgot to provide a Model instance inside the mvpModule class.
    Great work!

  3. Palash says:

    where is DaggerAppComponent Class.I didn’t get this class in whole project

    1. Anupam says:

      It gets auto-generated when you build the code.

  4. mahmudin says:

    you forget to add android:name=”.initApplication” to manifest

  5. Alireza K says:

    thank you very much, How can i use this method for fragments in an activity

  6. Anggit says:

    really complex, is it the best way to using dagger with mvp? because i think it can take so much time make job get done (in my opinion). how about you?

Leave a Reply

Your email address will not be published. Required fields are marked *

close
Generic selectors
Exact matches only
Search in title
Search in content
Search in posts
Search in pages