MVP with RxJava in Android

In my last tutorial, I have written many articles to use RxJava in android. RxJava is really very hot into the market today and many developers are using reactive functionality in our applications. It provides much more features to build the awesome app. The basic of RxJava I have already explained in my earlier articles. If you have not checked before then please go ahead and check all my posted article about RxJava in Android. Here are details link  RxJava in Android part1 and RxJava in Android part2.

In this article, We will learn how to build the android app by using RxJava and MVP. MVP stand for Model View Presenter. MVP is very popular design pattern in android. Prior we practiced old design pattern name MVC (Moel View Controller). Now many of developers they are using MVP.

Let's discuss Why MVP ? and Why not MVC?

Here is the diagram that represents the MVC and MVP design pattern.



So Here are different - different aspect used in the design pattern. First, we need to know What is the mean of those aspects.

Model: Model represent the collection of classes that represent the business model and data model. It means that how the data can manipulate.

View: View represents the user interface component like Button, TextView etc. View display the data from the model and also change the model from the user interface.

Controller: Controller work is like a mediator. It processes the incoming request from model to change in view or get input from the view to make the change in the model.

Presenter: Presenter is responsible for marking all user interface event on behalf of View. It processes the user data from the model and reflects on View and similarly, It processes the view action of the user and makes the change into the model. Here there is the direct communication from model to view and vice Versa.

Now the people are using very complex user interface and business model. In MVC it creates a lot of problems that recovered in MVP. MVC design pattern provides the communication through the Controller but this is not mandatory. It can be from model to view also and vice versa. It will create the problem in future if we need to add any new feature in existing code or required maintenance of our application. In this case, a developer has to face many issues. They will get stuck in resolving the view hierarchy issue rather than focus on business logic. Even debug and Unit testing will be very difficult for the developer in single class or model.

MVP design pattern provides the solution to those issues. It is easier for adding any new feature into existing model. For example, Suppose in one of the apps I used offline data sync it means storing the data into DB and fetching the data from the DB to reflect into view. If we required syncing these data with a server for online. Then for this, we do not require too many changes into code. A developer easily adds this feature in presenter class and nothing required to change the view or model code. It makes our life pretty easy to add any new features or maintenance.

MVP  has a single presenter for every activity or fragment to communication. Single presenter works with any view at a time. Model is not talking with View. It divides our code into different- different model with a single presenter. So it's easy for the developers to debug and unit testing of a single model.

Ok Now, let's create an android project by using RxJava with MVP design pattern. Here I am using the Green dao database for storing data. Ok Lets I have stored some data and want to show on recyclerView. Here are the details of the MVP design structure.


For using RxJava we need to add these two dependencies into our project. Reactive functionality are available in this library.

compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
compile 'io.reactivex.rxjava2:rxjava:2.0.8'
First, we need to create the base presenter and base view interface.

  

/**
 * Created by sunil on 4/6/2017.
 */

public interface BasePresenter {

    void subscribe();
    void unSubscribe();
}
Let's create base view interface.

public interface BaseView {

    void setPresenter(T presenter);
}

LoadArticleContact.java

  

/**
 * Created by sunil on 4/7/2017.
 */

public class LoadArticleContact {

    public interface Presenter extends BasePresenter {
        void deleteArticle(@NonNull String id);
        void loadArticle(boolean forceUpdate);
    }
  public interface View extends BaseView {
        void onLoading();
        void onLoadOk(List<Articles> articles);
void onLoadError(String msg); void onLoadFinish(); void deleteArticle(@NonNull String id); } }

LoadArticlePresenter .java

  
import android.support.annotation.NonNull;

import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
import com.sunil.data.model.Article;
import com.sunil.data.source.ArticleLocalDataSource;

import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;

import java.util.List;

import io.reactivex.Flowable;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.functions.Action;
import io.reactivex.functions.Consumer;
import timber.log.Timber;

/**
 * Created by sunil on 4/7/2017.
 */

public class LoadArticlePresenter implements LoadArticleContact.Presenter{
    
    @NonNull
    private final LoadArticleContact.View mArticleView;

    private CompositeDisposable mCompositeDisposable;

    private ArticleLocalDataSource mArticleLocalDataSource;

    public LoadArticlePresenter(@NonNull ArticleLocalDataSource articleLocalDataSource, @NonNull LoadArticleContact.View articleView) {
        mArticleLocalDataSource = articleLocalDataSource;
        mArticleView = articleView;
        mArticleView.setPresenter(this);
        mCompositeDisposable = new CompositeDisposable();
    }


    @Override
    public void deleteArticle(@NonNull String id) {
         Preconditions.checkNotNull(id);
         mArticleLocalDataSource.deleteArticle(id);
         mArticleView.deleteArticle(id);
    }

    @Override
    public void loadArticle(boolean forceUpdate) {
        if (forceUpdate) {
            Flowable> listFlowable = mArticleLocalDataSource.getArticles();
            listFlowable.doOnSubscribe(new Consumer() {
                @Override
                public void accept(@io.reactivex.annotations.NonNull Subscription subscription) throws Exception {

                }
            }).subscribe(new Consumer>() {
                @Override
                public void accept(@io.reactivex.annotations.NonNull List<Article> articles) throws Exception {
                    mArticleView.onLoadOk(articles);
                }
            }, new Consumer() {
                @Override
                public void accept(@io.reactivex.annotations.NonNull Throwable throwable) throws Exception {
                    Preconditions.checkNotNull(throwable);
                    mArticleView.onLoadError(throwable.getMessage());
                }
            }, new Action() {
                @Override
                public void run() throws Exception {
                    mArticleView.onLoadFinish();
                }
            });

        }
    }

    @Override
    public void subscribe() {
        if (mCompositeDisposable == null) {
            mCompositeDisposable = new CompositeDisposable();
        }
    }

    @Override
    public void unSubscribe() {
        mArticleView.onLoadFinish();
        mCompositeDisposable.clear();

    }
}

LoadArticleFragment.java

  
import android.app.Fragment;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;

import com.google.common.base.Preconditions;
import com.sunil.data.model.Article;
import com.sunil.data.source.ArticleLocalDataSource;
import com.sunil.mvprxjava.adapter.ArticleAdapter;
import com.sunil.mvprxjava.ui.addarticlle.ArticleContract;
import com.sunil.navigationslideroot.R;

import java.util.ArrayList;
import java.util.List;

import butterknife.BindView;
import butterknife.ButterKnife;

/**
 * Created by sunil on 4/7/2017.
 */

public class LoadArticleFragment extends Fragment implements LoadArticleContact.View, ArticleAdapter.onItemClickListener{

    @BindView(R.id.recyclerView)
    RecyclerView recyclerView;

    private LoadArticleContact.Presenter mPresenter;

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

    @Override
    public void onResume() {
        super.onResume();
        mPresenter.subscribe();
    }

    @Override
    public void onPause() {
        super.onPause();
        mPresenter.unSubscribe();
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        // do things if you want to create only first time when activity created.
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View root = inflater.inflate(R.layout.fragment_load_article, container, false);
        ButterKnife.bind(this, root);
        mPresenter = new LoadArticlePresenter(new ArticleLocalDataSource(), this);
        if (mPresenter  != null){
            mPresenter.loadArticle(true);
        }

        return root;
    }


    @Override
    public void onLoading() {

    }

    @Override
    public void onLoadOk(List<Article> articles) {
Preconditions.checkNotNull(articles); // call adapter ArticleAdapter articleAdapter = new ArticleAdapter(getActivity(), articles, this); recyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); recyclerView.setAdapter(articleAdapter); } @Override public void onLoadError(String msg) { Toast.makeText(getActivity(), msg, Toast.LENGTH_LONG).show(); onLoadFinish(); } @Override public void onLoadFinish() { // finish the dialog if using Toast.makeText(getActivity(), "Completed", Toast.LENGTH_LONG).show(); } @Override public void deleteArticle(@NonNull String id) { Toast.makeText(getActivity(), "Deleted", Toast.LENGTH_LONG).show(); } @Override public void setPresenter(LoadArticleContact.Presenter presenter) { mPresenter = Preconditions.checkNotNull(presenter); } @Override public void itemRemoveClick(long id) { Preconditions.checkNotNull(id); mPresenter.deleteArticle(String.valueOf(id)); } }

This is required code for the MVP design. Here I used the local database for storage. In future, if I required the same thing for remote storage to fetch the data remotely. Then it does not require many code change. A developer will look into the presenter class to add remote or network call rather than touch any view or model class. He just required focusing on the business model.


Thanks for reading this post.

Comments

Popular posts from this blog

NavigationView Drawer Expandable menu Item in ANdroid

Service LifeCycle

Custom SeekBar (Discrete SeekBar) in android