Dagger2 dependency injection in MVP android

Sharing is caring!

Dagger 2 is awesome dependency injection library which is used in android. In this tutorial, we will try to understand what is Dagger2 and why is required dependency injection and how to use in building the architectural based android app? Even Dagger2 is much comfortable with using RxJava Observable. Sounds crazy 🙂 Let’s see first of all what is Dagger 2?

What is Dagger 2?

Dagger 2 is awesome java library which provides the alternative way of Object instantiation.  You do not worry about the constructor with a different type of argument. Dagger 2 will take care automatically based on the qualifier. What you need to do just used annotated @Inject then all required object will be created and assigned automatically.

Why is required dependency injection and what are the benefits to using this?

While building any android app we are instantiating at least 40 objects or more in any of the android project. But later if those are required to refactor, change argument type and change in the number of arguments, it will create many problems in many places in existing code. But Dragger 2 will handle this type of problem very smartly.

Using dagger 2 having a lot of benefits, for example, Maintainability, testability, low coupling, easy singletons, less boilerplate code and order of object instantiation etc.

I will describe its benefits into deep to explaining with awesome features of Dagger2.  But to understand the core features of Dagger2 we need to look at the architectural diagram. Here is detail:

Application Components are application specific objects like Network communication, Sqlite Database component and SharedPrefernce etc that are injected into activity.  Its means object reference scope is available for application context.

Activity Components can have activity specific objects like Toolbars, Shared UI, or any custom based components want to inject into Fragments, Views, etc. It means object reference scope available for Activity context.  It avoids memory leak kind of issue.

Dagger 2 is annotation based java library. Ok great, Let’s see what are fields available in Dagger2.

@Scope – It means any particular object availability indoors scope. Dagger 2 has a more concrete way to do scoping through custom annotations. It can be @PerApplication @PerActivity, @PerFragment etc. By default, it provides only @singletone scope for root component.

@Singletone – It means that if any object is already created then do not need to recreate it, we can reuse this object throughout the application. For example, SharedPreference which we can reuse object throughout the application anywhere.

@Module -A module is the part of the application that provides some functionality as a dependency for injecting.

@Component – It is an interface and also part of an application to consume some functionality. It enabled for selected modules to perform as a dependency.

@Provide – This annotation is used inside the module it means it will tell to Dagger to instantiating the object for dependency.

@Inject – It can be used on a constructor, field or a method. Dagger will inject the object for a class of a specific module.

And much more annotated features are available you can check from official documentation.

How can we use Dagger2?

Ok, Let’s see a sample project to use Dagger2 in MVC Design pattern. In this sample project, we will store the article information into shared preference and fetched from shared preference to show the detail on view. First of all, you need to add some of the dependency into your build.gradle file as module level.

 compile "com.google.dagger:dagger:2.9"
 annotationProcessor "com.google.dagger:dagger-compiler:2.9"
 provided 'javax.annotation:jsr250-api:1.0'
 compile 'javax.inject:javax.inject:1'

In the next step, we need to initialize the dagger builder in Main Application class.

public class MainApplication extends Application{

    protected ApplicationComponent mApplicationComponent;

    @Override
    public void onCreate() {
        super.onCreate();
        setupInjector();
    }

    public ApplicationComponent getApplicationComponent() {
        return mApplicationComponent;
    }

    private void setupInjector() {
        mApplicationComponent = DaggerApplicationComponent.builder()
                .applicationModule(new ApplicationModule(this)).build();
        mApplicationComponent.inject(this);
    }

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

Now you need to create your custom scope. As I am creating two scopes one is for @PerAppliction and other for @PerActivity.

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

In the next step, we need to create SharedPrefsUtil class as a @Singleton that we can reuse the Object throughout the application anywhere and will use a @Inject constructor for instantiating the object of this.

@Singleton
public class SharedPrefsUtil {
    private SharedPreferences mSharedPreferences;
    public static String PREF_KEY = "dagger_key";

    @Inject
    public SharedPrefsUtil(SharedPreferences sharedPreferences) {
        mSharedPreferences = sharedPreferences;
    }

    public void put(String key, String value) {
        mSharedPreferences.edit().putString(key, value).apply();
    }

    public String get(String key, String defaultValue) {
        return mSharedPreferences.getString(key, defaultValue);
    }
}

Now in the next step, I need to create module context based on Application and Activity Module.

@Module
public class ApplicationModule {

    private final MainApplication mainApplication;

    public ApplicationModule(MainApplication mainApplication) {
        this.mainApplication = mainApplication;
    }

    @Provides
    public Application provideApplication() {
        return mainApplication;
    }


    @Provides
    SharedPreferences provideSharedPrefs() {
        return mainApplication.getSharedPreferences("dragger-prefs", Context.MODE_PRIVATE);
    }
}
@Module
public class ActivityModule {

    private Activity mActivity;

    public ActivityModule(Activity activity) {
        mActivity = activity;
    }


    @Provides
    Activity provideActivity() {
        return mActivity;
    }
}

Great 🙂 Now I will create the component to inject this module for construct the object.

@Singleton
@Component(modules = ApplicationModule.class)
public interface ApplicationComponent {

    void inject(MainApplication mainApplication);

    Application getApplication();

    SharedPrefsUtil getSharedPrefsUtil();

}

ActivityComponent says to Dagger to generate the code of injection into MainActivity.

@PerActivity
@Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class)
public interface ActivityComponent {

    void inject(MainActivity mainActivity);
}

Now you need to build this project to create the Dagger dependency generator class. Once Dagger created a generated class then we can inject this generated call to set the dependency.

public class MainActivity extends AppCompatActivity {

    @Inject
    public  SharedPrefsUtil sharedPrefsUtil;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        injectDependency();
        if (savedInstanceState == null){
            ActivityUtil.addFragmentToActivity(getFragmentManager(), ArticleFragment.newInstance(), R.id.frame, "ArticleFragment");
        }
    }

    private void injectDependency() {
        ApplicationComponent applicationComponent = ((MainApplication)getApplication()).getApplicationComponent();
        ActivityComponent activityComponent = DaggerActivityComponent.builder()
                .applicationComponent(applicationComponent)
                .activityModule(new ActivityModule(this))
                .build();
        activityComponent.inject(this);
    }

}

Here DaggerActivity Component is the generated class by Dagger. You can go and check the details that what does this class?

In this demo project I am using to store the Article information into shared preference for that I need to create the Article Model.

public class Article {

    private String articleName;
    private String articleDescription;

    public Article(){

    }

    public Article(String articleName, String articleDescription) {
        this.articleName = articleName;
        this.articleDescription = articleDescription;
    }

    public String getArticleName() {
        return articleName;
    }

    public String getArticleDescription() {
        return articleDescription;
    }
}

I am using MVP design pattern then I need to create the ArticleContract class.

public class ArticleContract {

    public interface View extends BaseView{

        void onLoadArticleOk(Article article);
        void onSavedArticle();

    }

    public interface Presenter extends BasePresenter<View> {

        void addArticle(Article article, SharedPrefsUtil sharedPrefsUtil);

        void showArticle(SharedPrefsUtil sharedPrefsUtil);

    }
}

In the next step, I need to create the @ArticleModule and @ArticleComponent which scope will be @PerActivity.

@Module
public class ArticleModule {

    @Provides
    Article getArticle() {
        return new Article();
    }


    @Provides
    ArticleContract.Presenter getArticlesPresenter() {
        return new ArticlePresenter();
    }
}

ArticleComponent says to Dagger to generate the code of injection into ArticleFragment.

@PerActivity
@Component(dependencies = ApplicationComponent.class, modules = ArticleModule.class)
public interface ArticleComponent {
    void inject(ArticleFragment mainFragment);
}

OK great. We can inject this module in ArticleFragment class and construct the object of ArticlePresenter.

public class ArticleFragment extends Fragment implements ArticleContract.View {

    @BindView(R.id.title)
    EditText titleEditText;
    @BindView(R.id.description)
    EditText descriptionEditText;
    @BindView(R.id.add)
    Button add;
    @BindView(R.id.show)
    Button show;
    @BindView(R.id.titleView)
    TextView titleView;
    @BindView(R.id.descView)
    TextView descView;
    private View mRootView;

    SharedPrefsUtil sharedPrefsUtil;

    @Inject
    ArticleContract.Presenter mPresenter;

    public static ArticleFragment newInstance() {
        return new ArticleFragment();
    }


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        injectDependency();
        mPresenter.attachView(this);
    }

    private void injectDependency() {
        ApplicationComponent applicationComponent = ((MainApplication) getActivity().getApplication()).getApplicationComponent();
        ArticleComponent articleComponent = DaggerArticleComponent.builder()
                .applicationComponent(applicationComponent)
                .articleModule(new ArticleModule())
                .build();
        articleComponent.inject(this);
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        mRootView = inflater.inflate(R.layout.fragment_main, container, false);
        ButterKnife.bind(this, mRootView);
        sharedPrefsUtil = ((MainActivity)getActivity()).sharedPrefsUtil;
        return mRootView;
    }

    @OnClick(R.id.add)
    public void addArticleClick() {
        String title = titleEditText.getText().toString().trim();
        String description = descriptionEditText.getText().toString().trim();
        if (title.isEmpty() && description.isEmpty()) {
            Toast.makeText(getActivity(), "Please enter details", Toast.LENGTH_LONG).show();
        } else {
            Article article = new Article(title, description);
            mPresenter.addArticle(article, sharedPrefsUtil);
        }
    }

    @OnClick(R.id.show)
    public void showArticleClick() {
        mPresenter.showArticle(sharedPrefsUtil);
    }


    @Override
    public void onLoadArticleOk(Article article) {
        titleView.setText("Title: "+article.getArticleName());
        descView.setText("Description: "+ article.getArticleDescription());
    }

    @Override
    public void onSavedArticle() {
     Toast.makeText(getActivity(), "Saved", Toast.LENGTH_LONG).show();
    }
}

Here is the result.

Ok, You have done so for. I  need to wrap up this post now otherwise it will go beyond this post. OMG for simple to store the Article information into the shared preference I have created so many classes for injecting dependency in MVP design.

The conclusion is that It does not matter how many class files need to create the most important that our code will be in well structured in  MVP and Dagger2 and we do not worry about any refactor or changes in code in future.

Because Dagger divides every module in a separate module that’s why it makes the Junit testing and changes in the code for a specific module is pretty easy and simple and every developer loving this. If you are using IntelliJ IDEA then use this plugin for a better debugging experience. It will create visual connections between a @Inject object and the @Provides method that creates it.

Here is the Github link for a Dagger2 project.

Ok friends see you in my next tutorial. Please do not forget to subscribe to get news update for this tutorial and feedback on below comment section. Happy weekend and Happy coding 🙂

0 0 votes
Article Rating
Dagger2 dependency injection in MVP android
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Scroll to top
0
Would love your thoughts, please comment.x
()
x