Android Architecture

Architect Android Apps with MVP, Dagger, Retrofit & Rxjava

In this article, I’m going to explain the implementation of MVP architecture using Dagger 2, ButterKnife, Room Persistence, Rxjava 2, RxAndroid, Retrofit, logging and debugging. In this Android tutorial, we will build project that contains Architect Android Apps with MVP, Dagger, Retrofit & Rxjava.

I will share repository that contains all above library implementation following best practices. We create superior design in such way that it could be inherited and maximize the code reuse.

To order to build a clean code in the MVP architecture, some tool and framework are needed. These are Dagger2, Retrofit, RxJava, RoomPersisstense It will be good to understand how each of these libraries plays a specific part in the role to build better code. There almost industries standard of build a modern application for Android.

Prerequisite

As this article we are using many libraries, So you need to have knowledge of each one library. because in this sample app all library integrated with depends on one another. I recommend you got through MVP Architect Sample Application Tutorials Series.

MVP Architect Sample Application Tutorials Series

  1. MVP :- https://androidwave.com/android-mvp-architecture-for-beginners-demo-app/
  2. Dagger2 :- https://androidwave.com/dagger2-android-example/
  3. RxJava2:- https://androidwave.com/rxjava-rxandroid-tutorials/
  4. Retrofit:- https://square.github.io/retrofit/
  5. Room Persistence:- https://androidwave.com/working-with-room-persistence-library/
  6. Debugging and Logging:- https://androidwave.com/useful-tools-for-logging-debugging-in-android/
  7. Downloadable Font:- https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts
  8. Globally Error Handling – https://androidwave.com/retrofit-globally-error-handling/

The app has following packages:

  1. data: It contains all the data accessing and manipulating components.
  2. Room Persistence (Local Database )
  3. Networking stuff (REST APIs call)
  4. Shared Preferences.
  5. : Dependency providing classes using Dagger 2.
    1. Components
    2. Module  
  6. ui: View classes along with their corresponding Presenters.
  7. Views
  8. Custom Components
  9. Activity, fragment
  10. : Services for the application.
    1. All service and Job IntentService should be placed here.
  11. utils: Utility classes.
    1. All utility class write in this package
To order to build a clean code in the MVP architecture, some tool and framework are needed. These are Dagger2, Retrofit, RxJava, RoomPersisstense

How to use this repo

Project Setup

Go to the root directory of workspace where you want place own project and just take a clone from GitHub repo using below command. If you face any issue while taking clone, Read this article

$ git clone https://github.com/droidwave/MVP-Architect-Android-Apps.git

After taking successful clone you have to do following changes

Brief Intro of Repo

In this repo, we have created a separate package for each module or functionality. For all user interface part is placed in UI package. Inside the UI package, we have created separate package for each small app module or activity. I would like to suggest just create a new package for new activity. For example for login module create login, for main activity create main, for profile create a profile package. such as below diagram.

To order to build a clean code in the MVP architecture, some tool and framework are needed. These are Dagger2, Retrofit, RxJava, RoomPersisstense
Follow below step for create a new activity
  • Just create a new package name like profile. Now go to file menu and create a new activity named is ProfileActivity.
  • Now extends the BaseActivity instance of AppCompatActivity and override setUp() methods.
package com.androidwave.cleancode.ui.profile;


import android.os.Bundle;

import com.androidwave.cleancode.R;
import com.androidwave.cleancode.ui.base.BaseActivity;

public class ProfileActivity extends BaseActivity {

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

    @Override
    protected void setUp() {

    }
}

  • Create a new interface name is ProfileMvpView which extends MvpView and declare all UI operation methods here such as updateProfile(UserProfile profile) for as per need
package com.androidwave.cleancode.ui.profile;

import com.androidwave.cleancode.data.network.pojo.UserProfile;
import com.androidwave.cleancode.ui.base.MvpView;

/**
 * Created on : Feb 17, 2019
 * Author     : AndroidWave
 */
public interface ProfileMvpView extends MvpView {
    void updateProfile(UserProfile profile);
}

  • Furthermore, create a new interface with names PofileMvpPresenter<V extends ProfileMvpView> which extends MvpPresenter<V>. The view is here type of View. Here you declare all data related methods. For example network operation, fetching data from local DB etc.
package com.androidwave.cleancode.ui.profile;

import com.androidwave.cleancode.ui.base.MvpPresenter;

/**
 * Created on : Feb 17, 2019
 * Author     : AndroidWave
 */
public interface ProfileMvpPresenter<V extends ProfileMvpView> extends MvpPresenter<V> {
    void onViewPrepared();
}

  • Create a new presenter with name ProfilePresenter which extends BasePresenter and implementing PofileMvpPresenter. After that override super constructor and annotated with @Inject annotations like below
  • Now open ProfileActivity and implement ProfileMvpView.
package com.androidwave.cleancode.ui.profile;

import com.androidwave.cleancode.data.DataManager;
import com.androidwave.cleancode.ui.base.BasePresenter;
import com.androidwave.cleancode.utils.rx.SchedulerProvider;

import io.reactivex.disposables.CompositeDisposable;

/**
 * Created on : Feb 17, 2019
 * Author     : AndroidWave
 */
public class ProfilePresenter<V extends ProfileMvpView> extends BasePresenter<V>
        implements ProfileMvpPresenter<V> {

    public ProfilePresenter(DataManager manager, SchedulerProvider schedulerProvider, CompositeDisposable compositeDisposable) {
        super(manager, schedulerProvider, compositeDisposable);
    }

    @Override
    public void onViewPrepared() {
        getMvpView().showLoading();
        getCompositeDisposable().add(getDataManager()
                .getUserProfile(String.valueOf(getDataManager().getUserId()))
                .subscribeOn(getSchedulerProvider().io())
                .observeOn(getSchedulerProvider().ui())
                .subscribe(response -> {
                    if (!isViewAttached()) {
                        return;
                    }
                    getMvpView().hideLoading();
                    /**
                     * Update view here
                     */
                    getMvpView().updateProfile(response.getData());
                }, error -> {
                    if (!isViewAttached()) {
                        return;
                    }
                    getMvpView().hideLoading();

                    /**
                     * manage all kind of error in single place
                     */
                    handleApiError(error);
                }));
    }
}
  • After that, you have to write a DI code for providing presenter here > base package => di =>module => ActivityModule
    @Provides
    @PerActivity
    ProfileMvpPresenter<ProfileMvpView> provideProfilePresenter(ProfilePresenter<ProfileMvpView> presenter) {
        return presenter;
    }
  • Finally, Open ProfileActivity and attached presenter with view
package com.androidwave.cleancode.ui.profile;


import android.os.Bundle;

import com.androidwave.cleancode.R;
import com.androidwave.cleancode.data.network.pojo.UserProfile;
import com.androidwave.cleancode.ui.base.BaseActivity;

import javax.inject.Inject;

public class ProfileActivity extends BaseActivity implements ProfileMvpView {
    @Inject
    ProfileMvpPresenter<ProfileMvpView> mPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_profile);
        getActivityComponent().inject(this);
        mPresenter.onAttach(ProfileActivity.this);
        setUp();
    }

    @Override
    protected void setUp() {

    }

    @Override
    public void updateProfile(UserProfile profile) {

    }
}
  • Open ActivityComponent from di =>components => ActivityComponent and add bellow code
    void inject(ProfileActivity profileActivity);

Finally, Your activity is ready to run, Almost same step you have to follow for fragment.

How to add RestApi

We have to follow some basic step to add RestAPIs in this repos, as you seeing BaseDataManager class is responsible to handling all type data such as Rest APIs , PreferencesManager and local DB also.

  • Just navigate to base package=> data => network => NetworkService and add retrofit calling methods
package com.androidwave.cleancode.data.network;

import com.androidwave.cleancode.data.network.pojo.FeedItem;
import com.androidwave.cleancode.data.network.pojo.LoginRequest;
import com.androidwave.cleancode.data.network.pojo.UserProfile;
import com.androidwave.cleancode.data.network.pojo.WrapperResponse;

import java.util.List;

import io.reactivex.Single;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.Path;

/**
 * Created on : Jan 19, 2019
 * Author     : AndroidWave
 */
public interface NetworkService {
    /**
     * @return Observable feed response
     */
    @GET("feed.json")
    Single<WrapperResponse<List<FeedItem>>> getFeedList();


    @POST("login")
    Single<WrapperResponse<UserProfile>> doLoginApiCall(@Body LoginRequest mRequest);

   // here is api that fetching user details    
    @GET("user/profile/{user_id}")
    Single<WrapperResponse<UserProfile>> getUserProfile(@Path("user_id") String userId);
}

  • Now open RestAPI manager and one methods for dealing getProfile API
package com.androidwave.cleancode.data.network;

import com.androidwave.cleancode.data.network.pojo.FeedItem;
import com.androidwave.cleancode.data.network.pojo.LoginRequest;
import com.androidwave.cleancode.data.network.pojo.UserProfile;
import com.androidwave.cleancode.data.network.pojo.WrapperResponse;

import java.util.List;

import io.reactivex.Single;

public interface RestApiHelper {

    Single<WrapperResponse<UserProfile>> doLoginApiCall(LoginRequest request);

    Single<WrapperResponse<List<FeedItem>>> getFeedList();

    // add this line of code
    Single<WrapperResponse<UserProfile>> getUserProfile(String userId);
}

  • Now override this methods in all subclass . Open RestApiManager and add below line
    @Override
    public Single<WrapperResponse<UserProfile>> getUserProfile(String userId) {
        return mService.getUserProfile(userId);
    }
  • Finally, Open base BaseDataManager and add an unimplemented method like below
   @Override
    public Single<WrapperResponse<UserProfile>> getUserProfile(String userId) {
        return mApiHelper.getUserProfile(userId);
    }

After following all above just RUN the project and use app, If you have any queries, feel free to ask them in the comment section below. Happy Coding 🙂

Download Architect Android Apps with MVP, Dagger, Retrofit & Rxjava

1
Leave a Reply

avatar
1 Comment threads
0 Thread replies
0 Followers
 
Most reacted comment
Hottest comment thread
1 Comment authors
Marouane KAFOU Recent comment authors
  Subscribe  
newest oldest most voted
Notify of
Marouane KAFOU
Guest
Marouane KAFOU

Can the tutorial go through all files please