MVVM with RxJava in Android

In my last tutorial, we have learned the MVP design pattern which is most popular to used to build any android application. In that article, we had practiced building an application by using RxJava in MVP design pattern. If you are not checked my last articled which is based on MVP design pattern then I would recommend checking this here MVP with RxJava in android. Wow, that's great.

Today We got one more day lets make this day more awesome to learn something new. In this article, we will learn one more design pattern which comes from data binding in android that is called MVVM (Model View ViewModel). Model view ViewModel is similar like MVP.


Here we need to understand the what are the aspects in this diagram. Let me explain one by one.

Model: Model represents the classes that used for creating the business model of an application. It means how the data can manipulate.

View: View represents the user interface means Button or TextView. View display the data from the model.The view which binds with Observable variables and action triggered for ViewModel.

ViewModel: ViewModel is a middle layer for interaction between the View and model. Its responsibility is preparing the Observable data which is needed to view and also hooks for model event triggered from view.
This pattern is using the data binding concept which means that multiple views contain with single ViewModel. ViewModel should have all information which is required for the view.  In this case, ViewModel that can be reused for multiple views. For Single ViewModel interaction for a module, our debug and unit testing becomes easy. A developer can find the issue easily and it makes easy to add any new feature in existing code. Overall it makes our life pretty easy and simple.

Let's create an android project by using RxJava in MVVM design pattern. The code structure will be exactly same as we have shown in MVP. Please check this structure for MVVM.


Here I am using the green dao ORM for a database. In this project, I am trying store articles information and fetch to display those articles on recyclerView. Let's. Lets create the base interface and BaseFragment class.

public interface BaseView {

    interface View {

    }

    interface ViewModel {

        void onViewResumed();
        void onViewAttached(@NonNull BaseView.View viewCallback);
        void onViewDetached();
    }
}

BaseFragment.java

public abstract class BaseFragment extends Fragment implements BaseView.View{


    protected abstract BaseView.ViewModel getViewModel();

    @Override
    public void onResume() {

        super.onResume();
        getViewModel().onViewResumed();
    }

    @Override
    public void onStart() {

        super.onStart();
        getViewModel().onViewAttached(this);
    }

    @Override
    public void onStop() {

        super.onStop();
        getViewModel().onViewDetached();
    }
}

LoadArticleContract.java

public class LoadArticleContract {

    interface View extends BaseView.View {

        void onLoading();
        void onLoadingError(String msg);
        void onLoadingFinish();
        void onLoadingOK(List<Article> articles);
} interface ViewModel extends BaseView.ViewModel { void deleteArticle(@NonNull String id); void loadArticle(boolean forceUpdate); } }

LoadArticleViewModel.java

public class LoadArticleViewModel implements LoadArticleContract.ViewModel{

    @NonNull
    private LoadArticleContract.View mArticleView;

    private ArticleLocalDataSource mArticleLocalDataSource;

    public LoadArticleViewModel(@NonNull ArticleLocalDataSource articleLocalDataSource, LoadArticleContract.View articleView) {
        mArticleLocalDataSource = articleLocalDataSource;
        this.mArticleView = articleView;
    }
    @Override
    public void deleteArticle(@NonNull String id) {

        if (mArticleView != null){
            Preconditions.checkNotNull(id);
            mArticleLocalDataSource.deleteArticle(id);
            mArticleView.onLoadingFinish();
        }

    }

    @Override
    public void loadArticle(boolean forceUpdate) {
        if (forceUpdate){
            Flowable<List<Article>> 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> articleList) throws Exception {
mArticleView.onLoadingOK(articleList); } }, new Consumer() { @Override public void accept(@io.reactivex.annotations.NonNull Throwable throwable) throws Exception { Preconditions.checkNotNull(throwable); mArticleView.onLoadingError(throwable.getMessage()); } }); } } @Override public void onViewResumed() { } @Override public void onViewAttached(@NonNull BaseView.View viewCallback) { this.mArticleView = (LoadArticleContract.View) viewCallback; } @Override public void onViewDetached() { this.mArticleView = null; } }

ArticleLocalDataSource.java

public class ArticleLocalDataSource implements ArticleDataSource{

    private final  static String TAG = ArticleLocalDataSource.class.getSimpleName();

    private ArticleDao getArticleDao() {
        Timber.d(TAG, "getArticleDao()", Thread.currentThread().getName(), Thread.currentThread().getId());
        return GreenDaoDatabase.getInstance().getDaoSession().getArticleDao();
    }

    @Override
    public Flowable<List<Article>> getArticles() {
        return Flowable.fromCallable(new Callable<List<Article>>() {
            @Override
            public List<Article> call() throws Exception {
Timber.d(TAG, "getArticles()", Thread.currentThread().getName(), Thread.currentThread().getId()); List list = getArticleDao().loadAll();
Timber.d("getArticles: " + list.size()); return list; } }); } @Override public long saveArticle(@NonNull Article article) { Timber.d(TAG, "saveBook()", Thread.currentThread().getName(), Thread.currentThread().getId()); return getArticleDao().insertOrReplace(article); } @Override public void deleteArticle(@NonNull String id) { Timber.d(TAG, "deleteArticle()", Thread.currentThread().getName(), Thread.currentThread().getId()); getArticleDao().deleteByKey(Long.valueOf(id)); } }

Here in future, if required to store this articles information on the server and fetch those article from the server and save on locally. Then It will not require much code changes. We just need to look into ViewModel class rather than wasting any time on view or model class. It is a separate class that makes debug and unit testing becomes easy for the developer.

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