Skip to content

This project provide a basic Model-View-Presenter (MVP) architecture with the Dependency injection concept using Dagger 2 and Dagger-Android to externalize the creation of dependencies from the classes that use them.

License

Notifications You must be signed in to change notification settings

ShehabSalah/movieapp-mvp-dagger2

Repository files navigation

movieapp-mvp MVP + Dagger 2 (Dagger-Android)

Summary

This sample is based on the MVP sample and uses Dagger to externalize the creation of dependencies from the classes that use them.

Dependency injection is a concept valid for any programming language. The general concept behind dependency injection is called Inversion of Control. According to this concept a class should not configure its dependencies statically but should be configured from the outside.

A Java class has a dependency on another class, if it uses an instance of this class. We call this a _class dependency. For example, a class which accesses a logger service has a dependency on this service class.

Ideally Java classes should be as independent as possible from other Java classes. This increases the possibility of reusing these classes and to be able to test them independently from other classes.

If the Java class creates an instance of another class via the new operator, it cannot be used (and tested) independently from this class and this is called a hard dependency.

Dagger 2 API

Dagger 2 exposes a number of special annotations:

  • @Module for the classes whose methods provide dependencies
  • @Provides for the methods within @Module classes
  • @Inject to request a dependency (a constructor, a field, or a method)
  • @Component is a bridge interface between modules and injection

These are the most important annotations you need to know about to get started with dependency injection using Dagger 2.

Dagger 2 Workflow

To implement Dagger 2 correctly, you have to follow these steps:

  • Identify the dependent objects and its dependencies.
  • Create a class with the @Module annotation, using the @Provides annotation for every method that returns a dependency.
  • Request dependencies in your dependent objects using the @Inject annotation.
  • Create an interface using the @Component annotation and add the classes with the @Module annotation created in the second step.
  • Create an object of the @Component interface to instantiate the dependent object with its dependencies.

Dagger Android

We will briefly look at two annotations : @Binds and @ContributesAndroidInjector .

@Binds

This annotation provides a replacement of @Provides methods which simply return the injected parameter. Let’s take an example,

We have a MoviesPresenter which implements MoviesContract.Presenter. Without @Binds , the provider method for it will be something as follows :

@Module
public class MovieModule {
    
    @Provides
    public MoviesContract.Presenter provideMoviesPresenter(MoviesPresenter moviesPresenter) {
        return moviesPresenter;
    }
    
}

In the above case, we can instead use @Binds annotation and make the above method abstract :

@Module
public abstract class MovieModule {
    
    @Binds
    public abstract MoviesContract.Presenter provideMoviesPresenter(MoviesPresenter moviesPresenter);
    
}

Of course, we will also need to mark our Module as abstract in this case, which is more efficient than a concrete one and thus makes @Binds more efficient.

What makes our abstract Module more performing?

@Provides methods are instance methods and they need an instance of our module in order to be invoked. If our Module is abstract and contains @Binds methods, dagger will not instantiate our module and instead directly use the Provider of our injected parameter (MoviesPresenter in the above case).

What if your module contains both @Provides and @Binds methods?

In case, your Module has both @Provides and @Binds methods, you have two options :

  • Simplest would be to mark your @Provides instance methods as static .
  • If it is necessary to keep them as instance methods, then you can split your module into two and extract out all the @Binds methods into an abstract Module.
@Module
public abstract class MovieDetailsModule {
    
    @Provides
    static Movie provideMovie(DetailsActivity activity) {
        return activity.getIntent().getParcelableExtra(Constants.MOVIE_EXTRA);
    }
    
    @Binds
    abstract DetailsContract.presenter detailsPresenter(DetailsPresenter presenter);

}

@ContributesAndroidInjector

Dagger Android introduced this annotation which can reduce the Component , Binds , IntoSet , Subcomponent , ActivityKey , FragmentKey etc. boilerplate for you.

If you have a simple module like the following, you can then let dagger handle the rest.

@Module
public abstract class MovieModule {
    
    @Binds
    public abstract MoviesContract.Presenter provideMoviesPresenter(MoviesPresenter moviesPresenter);
    
}

All you need to do is write the following snippet inside the Module of the component, which is going to be the super-component of the generated DetailsComponent . Example, If you have an AppComponent and you want dagger to generate a DetailsSubcomponent for your DetailsActivity , you will write the following snippet inside your ActivityBindingModule .

@Module
public abstract class ActivityBindingModule {
    
    @ContributesAndroidInjector(modules = {MovieDetailsModule.class})
    abstract DetailsActivity detailsActivity();
    
}

We want Dagger.Android to create a Subcomponent which has a parent Component of whichever module ActivityBindingModule is on, in our case that will be AppComponent .

The beautiful part about this setup is that you never need to tell AppComponent that it is going to have all these subcomponents nor do you need to tell these subcomponents that AppComponent exists.

It’s a good practice to extract out a separate ActivityBindingModule for all such bindings and include it in the modules list of your AppComponent as follows :

@Singleton
@Component(modules = {
        ActivityBindingModule.class,
        AndroidSupportInjectionModule.class})
public interface AppComponent extends AndroidInjector<ApplicationClass> {
   
}

AndroidSupportInjectionModule.class is added because we used support Fragment. AndroidInjectionModule binds your app.Fragment to dagger. But If you want to use injection in v4.fragment then you should add AndroidSupportInjectionModule.class to your AppComponent modules.

Following is the boilerplate that gets generated for you when you use @ContributesAndroidInjector :

@Module(subcomponents = ActivityBindingModule_DetailsActivity.DetailsActivitySubcomponent.class)
public abstract class ActivityBindingModule_DetailsActivity {
  private ActivityBindingModule_DetailsActivity() {}
  
  @Binds
  @IntoMap
  @ActivityKey(DetailsActivity.class)
  abstract AndroidInjector.Factory<? extends Activity> bindAndroidInjectorFactory(
      DetailsActivitySubcomponent.Builder builder);
  
  @Subcomponent(modules = MovieDetailsModule.class)
  @ActivityScope
  public interface DetailsActivitySubcomponent extends AndroidInjector<DetailsActivity> {
    @Subcomponent.Builder
    abstract class Builder extends AndroidInjector.Builder<DetailsActivity> {}
  }
  
}

So, What @ContributesAndroidInjector do?

  • It generates the DetailsActivitySubcomponent .
  • It adds the necessary @Subcomponent annotation for us.
  • It adds an entry of ( DetailsActivity.class , DetailsActivitySubcomponent.Builder ) to the Map of Injector Factories used by DispatchingAndroidInjector . Dagger-Android uses this entry to build our DetailsActivitySubcomponent and perform injections for DetailsActivity .
  • Also, binds DetailsActivity to the object-graph.

Screenshots

Libraries

This version of the app uses some other libraries:

  • Dagger 2: externalize the creation of dependencies from the classes that use them
  • Picasso: used for loading, processing, caching and displaying remote and local images.
  • ButterKnife: used for perform injection on objects, views and OnClickListeners.
  • CardView: used for representing the information in a card manner with a drop shadow and corner radius which looks consistent across the platform.
  • RecyclerView: The RecyclerView widget is a more advanced and flexible version of ListView.
  • GSON: Gson is a Java library that can be used to convert Java Objects into their JSON representation. It can also be used to convert a JSON string to an equivalent Java object.
  • Retrofit: This library used to send HTTP request to the server and retrieve response.
  • ROOM Library: Room provides an abstraction layer over SQLite to allow fluent database access while harnessing the full power of SQLite.
  • BlurView Library: It blurs its underlying content and draws it as a background for its children.

The Movie DB API Key is required.

In order for the movieapp-mvp-clean app to function properly as of January 26th, 2018 an API key for themoviedb.org must be included with the build.

Include the unique key for the build by adding the following line to util/Constants.java or find the TODO Line.

API_KEY = "";

License

Copyright (C) 2018 Shehab Salah Open Source Project

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
  
  http://www.apache.org/licenses/LICENSE-2.0
  
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

About

This project provide a basic Model-View-Presenter (MVP) architecture with the Dependency injection concept using Dagger 2 and Dagger-Android to externalize the creation of dependencies from the classes that use them.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages