Dagger 2 and MVP Architecture

What we will be Learning?

  • Use dagger2 with RetrofitOkHttpGson and RxJava and RxAndroid
  • Use Model View Presenter(MVP) Architecture to decouple the Business logic and the underlying implementation of the logic to produce more cleaner code
  • Use RxJava to make request to our REST API
  • Use Retrofit to make call to a API and display the result
  • Learn about dependent components in dagger and creating custom scope for dependent components
  • We will be using Android studio and the code will be hosted on github

Lets get started

Step 1: Create a project with a Blank Activity

This slideshow requires JavaScript.

Step 2: Add the necessary dependency

Android Studio by default will not recognize a lot of generated Dagger 2 code as legitimate classes, but adding the android-apt plugin will add these files into the IDE class path and enable you to have more visibility. Add this line to your rootbuild.gradle:

dependencies {
     // other classpath definitions here
     classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
 }

Then make sure to apply the plugin in your app/build.gradle:

// add after applying plugin: 'com.android.application'  
apply plugin: 'com.neenbedankt.android-apt'

Add these three lines to your app/build.gradle file after this apply statement:

dependencies {
    // apt command comes from the android-apt plugin
    apt 'com.google.dagger:dagger-compiler:2.2'
    compile 'com.google.dagger:dagger:2.2'
    provided 'javax.annotation:jsr250-api:1.0'
}

In dependencies I added:

  • dagger library
  • dagger-compiler for code generation
  • javax.annotation for additional annotations required outside Dagger

After updating Dagger’s configuration, you can synchronize the project with the gradle files by clicking the button at the top.

dagger-2-gradle-sync.jpg

 

Step 3: Create packages to architecture our app in a clean way

  • We will be creating three packages called dataand mainscreen and util that we add the respective classes in these packages
  • We create two more packages inside dagger packages called asmodule and component
  • We will create all the files related to our mainscreen in the mainscreen package (MainActivity MainScreenContract)

This is how my app structure looks

2016-05-11_16h52_04.png

Step 4: Add Retrofit, OkHttp, Gson, RxJava libraries to your app/gradle file

 

This is how my build.gradle file looks after i add the dependency

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.3.0'

    //Retrofit
    compile 'com.squareup.retrofit2:retrofit:2.0.2'
    //OkHttp
    compile 'com.squareup.okhttp3:okhttp:3.2.0'
    compile 'com.squareup.okio:okio:1.7.0'

    //Gson
    compile 'com.google.code.gson:gson:2.6.2'
    compile 'com.squareup.retrofit2:converter-gson:2.0.1'

    //RxJava
    compile 'io.reactivex:rxjava:1.1.2'
    compile 'io.reactivex:rxandroid:1.1.0'
    compile 'com.squareup.retrofit2:adapter-rxjava:2.0.1'

    //Dagger
    apt 'com.google.dagger:dagger-compiler:2.2'
    compile 'com.google.dagger:dagger:2.2'
    provided 'javax.annotation:jsr250-api:1.0'
}

Also add INTERNET permission to your manifest

<uses-permission android:name="android.permission.INTERNET" />

Step 5: Create an AppModule that will provide context to other modules

  • Now because Retrofit and OkHttp will be used through the application we will created an AppModule that will be instantiated when the Application starts so that the app context is provided to Retrofit and OkHttp library
@Module
public class AppModule {
    Application mApplication;

    public AppModule(Application mApplication) {
        this.mApplication = mApplication;
    }

    @Provides
    @Singleton
    Application provideApplication() {
        return mApplication;
    }
}

Step 6: Create a NetModule that will provide Retrofit and OkHttp

@Module
public class NetModule {
    String mBaseUrl;

    public NetModule(String mBaseUrl) {
        this.mBaseUrl = mBaseUrl;
    }

    @Provides
    @Singleton
    SharedPreferences providesSharedPreferences(Application application) {
        return PreferenceManager.getDefaultSharedPreferences(application);
    }

    @Provides
    @Singleton
    Cache provideHttpCache(Application application) {
        int cacheSize = 10 * 1024 * 1024;
        Cache cache = new Cache(application.getCacheDir(), cacheSize);
        return cache;
    }

    @Provides
    @Singleton
    Gson provideGson() {
        GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
        return gsonBuilder.create();
    }

    @Provides
    @Singleton
    OkHttpClient provideOkhttpClient(Cache cache) {
        OkHttpClient.Builder client = new OkHttpClient.Builder();
        client.cache(cache);
        return client.build();
    }

    @Provides
    @Singleton
    Retrofit provideRetrofit(Gson gson, OkHttpClient okHttpClient) {
        Retrofit retrofit = new Retrofit.Builder()
                .addConverterFactory(GsonConverterFactory.create(gson))
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .baseUrl(mBaseUrl)
                .client(okHttpClient)
                .build();
        return retrofit;
    }
}

Step 7: Create NetComponent Interface

  • We create an Interface called NetComponent with AppModule and NetModule as its modules
  • Note that we will be using this Component as a dependency for our MainScreenComponent so we need retrofit as a dependency. Hence we expose its return type so that when MainScreen injects in any of the activities or fragments the MainScreenComponent can find and Inject Retrofit from this Module(May be confusing , continue reading to understand how this works)
@Singleton
@Component(modules = {AppModule.class, NetModule.class})
public interface NetComponent {
    // downstream components need these exposed with the return type
    // method name does not really matter
    Retrofit retrofit();
}

Step 8: Create a class App that extendApplication

  • Create a class App, extend it from Application class
  • Override onCreate method
public class App extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
    }
}
  • Because we are overriding the default Application class, we also modify the application name to launch App. This way your application will use this application class to handle the initial instantiation.
<application
      android:allowBackup="true"
      android:name=".App">
  • Create a private variable of type NetComponent and in onCreate build theNetComponent
  • Create a public method called getNetComponent() that will return the netcomponent variable whenever we require this in our App
public class App extends Application {
    private NetComponent mNetComponent;

    @Override
    public void onCreate() {
        super.onCreate();
        mNetComponent = DaggerNetComponent.builder()
                .appModule(new AppModule(this))
                .netModule(new NetModule("http://jsonplaceholder.typicode.com/"))
                .build();
    }

    public NetComponent getNetComponent() {
        return mNetComponent;
    }
}

Make sure to rebuild the project (in Android Studio, select Build > Rebuild Project) if you cannot reference the Dagger component.

Step 8: Create a POJO class and interface to make GET request to a REST API

  • http://www.jsonplaceholder.typicode.com is a fake REST api that providesJSON data
  • Create a POJO class called Post with title and body as String variables
  • Also add getters and setters using Android Studio
public class Post {
    private final Integer userId;
    private final Integer id;
    private final String title;
    private final String body;

    public Post(Integer userId, Integer id, String title, String body) {
        this.userId = userId;
        this.id = id;
        this.title = title;
        this.body = body;
    }

    public Integer getUserId() {
        return userId;
    }

    public Integer getId() {
        return id;
    }

    public String getTitle() {
        return title;
    }

    public String getBody() {
        return body;
    }
}

Step 9: In mainscreen package create an interface called MainScreenContract

  • This interface will have two more inner interface called View and Presenter
  • View interface holds all the methods which we will implement in our MainScreen View (i.e in our case MainActivity)
  • Presenter interface has all the methods that we will implement in our MainScreenPresenter (i.e only one method loadPost())
public interface MainScreenContract {
    interface View {
        void showPosts(List<Post> posts);

        void showError(String message);

        void showComplete();
    }

    interface Presenter {
        void loadPost();
    }
}

Step 10: Create MainScreenModule and MainScreenComponent

  • The purpose of creating MainScreenModule is to provide the View ( in our case MainActivity) to our Presenter when it is injected. The Presenter uses this view as a reference to our MainAvtivity to call the methods in the View (showError(),showComplete()) whenever the network operation are performed
  • It is a means through which the View and the Presenter Communicate with each other
  • In Dagger, two dependent components cannot share the same scope i.e (Our NetComponent  and MainScreenComponent cannot share the scope of @Singlton). So we create a custom  scope  called @CustomeScope to be used by our MainScreenComponent and MainScreenModule 

 

This is our MainScreenModule class

@Module
public class MainScreenModule {
    private final MainScreenContract.View mView;


    public MainScreenModule(MainScreenContract.View mView) {
        this.mView = mView;
    }

    @Provides
    @CustomScope
    MainScreenContract.View providesMainScreenContractView() {
        return mView;
    }
}

This is our MainScreenComponent

@CustomScope
@Component(dependencies = NetComponent.class, modules = MainScreenModule.class)
public interface MainScreenComponent {
    void inject(MainActivity activity);
}

This is our CustomeScope interface in util package

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

Step 9: Create MainPresenter which will implement the methods to make network calls(Your apps logic)

  • MainPresenter implements methods from MainScreenContract.Presenter
public class MainScreenPresenter implements MainScreenContract.Presenter {

    @Override
    public void loadPost() {

    }
}
  • We create an interface called PostService that makes a GET request at /posts and get a Observable of List of  Post objects as return type
private interface PostService {
    @GET("/posts")
    Observable<List<Post>> getPostList();
}
  • Our MainPresenter requires retrofit to make the network calls and the View to which it will display the results of the network calls. So we provide the Retrofit and View (MainActivity) by injecting them in the constructor.
  • So whenever a new object of MainPresenter is created Dagger will inject the dependency.
  • The retrofit dependency will be provided by NetComponent and the MainScreenContract.View dependency will be provided by the MainScreenComponent whenever we inject it in our Views(in our case we will inject it in MainActivity)
Retrofit retrofit;
MainScreenContract.View mView;

@Inject
public MainScreenPresenter(Retrofit retrofit, MainScreenContract.View mView) {
    this.retrofit = retrofit;
    this.mView = mView;
}
  • Now in our MainPresenter we implement the method loadPost() to make NetworkRequest using the retrofit object and PostService
  • As we will be using RxJava we get an Observable of a List of Post Objects
@Override
public void loadPost() {
    retrofit.create(PostService.class).getPostList().subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .unsubscribeOn(Schedulers.io())
            .subscribe(new Observer<List<Post>>() {
                @Override
                public void onCompleted() {
                    mView.showComplete();
                }

                @Override
                public void onError(Throwable e) {
                    mView.showError(e.getMessage());
                }

                @Override
                public void onNext(List<Post> posts) {
                    mView.showPosts(posts);
                }
            });
}
  • RxJavahas 3 methods for us
  • onNext() is called when we recieve the data
  • onError() is called obviously when we some error occures
  • onCompleted() is called when the network call is finished
  • We use the reference object mView and pass the results to the View via this reference variable which was injected in the View (i.e MainActivity)

This is how our MainPresenter class should look after all this

public class MainScreenPresenter implements MainScreenContract.Presenter {

    Retrofit retrofit;
    MainScreenContract.View mView;

    @Inject
    public MainScreenPresenter(Retrofit retrofit, MainScreenContract.View mView) {
        this.retrofit = retrofit;
        this.mView = mView;
    }

    @Override
    public void loadPost() {
        retrofit.create(PostService.class).getPostList().subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .unsubscribeOn(Schedulers.io())
                .subscribe(new Observer<List<Post>>() {
                    @Override
                    public void onCompleted() {
                        mView.showComplete();
                    }

                    @Override
                    public void onError(Throwable e) {
                        mView.showError(e.getMessage());
                    }

                    @Override
                    public void onNext(List<Post> posts) {
                        mView.showPosts(posts);
                    }
                });
    }

    private interface PostService {
        @GET("/posts")
        Observable<List<Post>> getPostList();
    }
}

Step 10: Create a Listview in activity_main.xml to display the list we receive

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".mainscreen.MainActivity">

    <ListView
        android:id="@+id/my_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>

Step 11: Implement MainScreenContract.View in our MainActivity

  • We implement the 3 methods in our MainActivity

    public class MainActivity extends AppCompatActivity implements MainScreenContract.View {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
    
        @Override
        public void showPosts(List<Post> posts) {
            
        }
    
        @Override
        public void showError(String message) {
    
        }
    
        @Override
        public void showComplete() {
    
        }
    }

 

  • We create the necessary variables to deal with the listview

ListView listView;
ArrayList<String> list;
ArrayAdapter<String> adapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    listView = (ListView) findViewById(R.id.my_list);
    list = new ArrayList<>();
}
  • We create a variable for our MainScreenPresenter and use @Inject annotation to inject it. We will use this variable to call the loadPost() method which is implemented in MainPresenter

@Inject
MainScreenPresenter mainPresenter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    listView = (ListView) findViewById(R.id.my_list);
    list = new ArrayList<>();

    DaggerMainScreenComponent.builder()
            .netComponent(((App) getApplicationContext()).getNetComponent())
            .mainScreenModule(new MainScreenModule(this))
            .build().inject(this);

    //Call the method in MainPresenter to make Network Request
    mainPresenter.loadPost();
}
  • Now we write the logic inside the methods  showPosts(List<Post> posts)  , showError(String message),showComplete()
  • These methods will be called by our MainPresenter using the mViewreference whenever the Network calls are made and the results will be passed to our MainActivity
@Override
public void showPosts(List<Post> posts) {
    //Loop through the posts and get the title of the post and add it to our list object
    for (int i = 0; i < posts.size(); i++) {
        list.add(posts.get(i).getTitle());
    }
    //Create the array adapter and set it to list view
    adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, list);
    listView.setAdapter(adapter);
}

@Override
public void showError(String message) {
    //Show error message Toast
    Toast.makeText(getApplicationContext(), "Error" + message, Toast.LENGTH_SHORT).show();

}

@Override
public void showComplete() {
    //Show completed message Toast
    Toast.makeText(getApplicationContext(), "Complete", Toast.LENGTH_SHORT).show();

}
  • This is how our MainActivity looks finaly
public class MainActivity extends AppCompatActivity implements MainScreenContract.View {
    ListView listView;
    ArrayList<String> list;
    ArrayAdapter<String> adapter;

    @Inject
    MainScreenPresenter mainPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        listView = (ListView) findViewById(R.id.my_list);
        list = new ArrayList<>();

        DaggerMainScreenComponent.builder()
                .netComponent(((App) getApplicationContext()).getNetComponent())
                .mainScreenModule(new MainScreenModule(this))
                .build().inject(this);

        //Call the method in MainPresenter to make Network Request
        mainPresenter.loadPost();
    }

    @Override
    public void showPosts(List<Post> posts) {
        //Loop through the posts and get the title of the post and add it to our list object
        for (int i = 0; i < posts.size(); i++) {
            list.add(posts.get(i).getTitle());
        }
        //Create the array adapter and set it to list view
        adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, list);
        listView.setAdapter(adapter);
    }

    @Override
    public void showError(String message) {
        //Show error message Toast
        Toast.makeText(getApplicationContext(), "Error" + message, Toast.LENGTH_SHORT).show();

    }

    @Override
    public void showComplete() {
        //Show completed message Toast
        Toast.makeText(getApplicationContext(), "Complete", Toast.LENGTH_SHORT).show();

    }
}

Final Output

Screenshot_2016-05-11-18-20-16.png

Conclusion

  • We use dagger 2 along with retrofit, OkHttp and Gson and RxJava to make a request to a REST API and display the data in the ListView
  • We used MVP architecture to decouple our Application logic with the underlining implementation details
  • Pros : MVP is great and recommended for large code base, and promotes cleaner code
  • Cons: MVP is writing too much of code to get things done (lot of boiler plate)

Github link for code

https://github.com/LadwaAditya/DaggerMVP-Tutorial

 Reference

Advertisements

49 thoughts on “Dagger 2 and MVP Architecture

  1. Extremely well-written article, very easy to follow along with too. Came here primarily for the DI but learned a lot more along the way.

    Like

  2. Hi Aditya,

    Great Article and Thanks for writing this with example. I have one concern here in the method

    @Override
    public void showPosts(List posts) {
    //Loop through the posts and get the title of the post and add it to our list object
    for (int i = 0; i < posts.size(); i++) {
    list.add(posts.get(i).getTitle());
    }
    //Create the array adapter and set it to list view
    adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, list);
    listView.setAdapter(adapter);
    }

    We are looping inside a view which i feel is not good any logic or condition ,loops should be written inside the presenter. View’s only job should be to display .What do you say?

    Like

    1. We are passing a List of posts from presenter to view . And we need to loop if we have to add the items to the recycler view.

      If we have to do the looping in the presenter than we need to pass the recycler view to the presenter to add the items which violates the MVP principle

      Like

      1. In NetModule there are methods

        SharedPreferences providesSharedPreferences(Application application) {
        Cache provideHttpCache(Application application) {
        Gson provideGson() {
        OkHttpClient provideOkhttpClient(Cache cache) {
        and
        Retrofit provideRetrofit(Gson gson, OkHttpClient okHttpClient) {

        we are using only the retrofit one inside NetComponent.

        So my doubt is the provideRetrofit create Retrofit Object does it get gson and okHttpClient injected automatically by dagger mechanism. Please explain

        Like

      2. Yes exactly thats the magic of dagger. It automatically determines all the dependency (if there are provided) and will injects them to the methods necessary to create new objects like Retrofit in your case.

        Like

  3. well written, however i do have one question:
    on step 10: MainScreenContract.View was defined as final, however, we know the view can be destroyed at any point of time, such as screen rotation, so how do we coop with such situation? do we need to get a new module when it happens? if so would we be able to reuse the presenter?

    thanks

    Like

    1. Hey, that’s a great question. Yes, a new view will be created when the view is destroyed. One way I did was cache my data and load data from persistent storage using repository pattern

      Like

    1. We can split by using another class like a DataManager which will delegate the calls from presenter to model. This approach is recommended.

      Like

  4. Hi Aditya,
    Nice article that inspiring me to create code more clean.
    Btw, I have a question when dealing with ListView or RecycleView versus RxJava.
    according to my experience, I’m used to use a Loader to take advantage of Android onConfigurationChange. so for example when the device reacts to orientation event, Loader manages the “data” which not trying to reload to remote server.
    Do you have a strategy to combine Loader with RXJava?

    Like

    1. Hello, that’s a great question. Well I haven’t tried combining a loader with RxJava, however in the example provided in the blog, when the device is rotated the data will be loaded again from the persistence.

      Like

  5. I have a little issue with the presenter. How does dependency injection work when it was injected into the MainActivity without declaring a method with @Provides annotation that returns MainPresenter.

    Like

    1. MainPresenter takes parameters that are provided by our dagger module, which we inject in the constructor. So whenever we call the presenter the dependencies will be provided by them

      Like

  6. How does MainScreenPresenter get injected here? Don’t you have to provide it in your module?

    @Inject
    MainScreenPresenter mainPresenter;

    Like

    1. Nice question. However we are doing a constructor injection. The parameters required to construct MainScreenPresenter are already provided by our dagger module

      Liked by 1 person

  7. I tried to use the same in my project , however it says cannot find DaggerNetComponent, can you please shine some light on this. I really like the article and want to learn more. But currently I am stuck at that point. DaggerNetComponent.

    Like

    1. Hello you will have to rebuild your project as those files are auto generated at compile time by dagger. So do a clean and rebuild project that should hopefully fix it

      Like

  8. Great guide, but i have really difficult time extracting views into fragments, i dont know what should i leave and what should i extract to fragment. I am very new to android, but i have some experience with dependancy injection in c#, but this is so different…

    Like

    1. Fragments are used only if an activity has multiple interaction with the user. Use them only if it’s needed otherwise go with activities.

      Like

  9. Good post. Clear and understandable.
    I implemented your code to retrieve data from MySQL database through PHP file, which generates JSON array. Even more, data retrieved in Cyrillic. It works.

    Like

  10. Thanks for the great article. Very well written indeed.

    One question; suppose my presenter has more than a few dependencies (which is provided by the ApplicationModule). What if I don’t want to provide all of them via the constructor? I would rather want to use field injection in that case (so that the constructor doesn’t take too many arguments). Is there a way around this? What would you suggest?

    Like

    1. In the application component you can add that presenter as root for injection(instead of the activity). You will then need an instance of AppComponent which you can use in the constructor to perform AppComponent.inject(this).

      Like

  11. Hi Aditya, excellent write up and easy to understand. I understood Dagger perfectly. However, I have a doubt. I tried implementing ButterKnife with this by binding the view in onCreate() method and unbinding in onDestroy() method as shown below, but I am getting an “Error: null” on running the app.

    public class MainActivity extends AppCompatActivity implements MainScreenContract.View {
    @BindView(R.id.my_list) ListView mListView;
    private Unbinder unbinder;

    ArrayList mList;
    ArrayAdapter mAdapter;

    @Inject
    MainScreenPresenter mMainScreenPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    unbinder = ButterKnife.bind(this);
    mList = new ArrayList();

    DaggerMainScreenComponent.builder()
    .netComponent(((App) getApplicationContext()).getNetComponent())
    .mainScreenModule(new MainScreenModule(this))
    .build()
    .inject(this);

    mMainScreenPresenter.loadPost();
    }

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

    @Override
    public void showPosts(List posts) {
    for(int i=0; i<posts.size(); i++) {
    mList.add(posts.get(i).getTitle());
    }
    mAdapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, mList);
    mListView.setAdapter(mAdapter);
    }

    @Override
    public void showError(String message) {
    Toast.makeText(getApplicationContext(), “Error: ” + message, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void showComplete() {
    Toast.makeText(getApplicationContext(), “Completed!”, Toast.LENGTH_SHORT).show();
    }
    }

    What am I missing here? Any suggestions?

    Like

    1. I think you are using a sub component. But also are you creating the main app component first? Or is it a dependent component?

      Like

  12. Thank you for writing this.
    I have a little trouble understanding how the MainActivity gets injected into the MainScreenPresenter.
    It is not initialized anywhere.. I cannot find provideMainScreenPresenter..

    Thanks.

    Like

    1. MainPresenter needs dependencies which are provided by dagger. So it’s a constructor injection which doesn’t require us to create an instance

      Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s