sirFunkenstine
sirFunkenstine

Reputation: 8495

Android MVVM Design Pattern

I have read in the recently released 'Android Best Practices' book that a good design pattern to use for android programming is MVVM. Having tried it myself on my latest project it does seem to be beneficial in separating code into more manageable sections.

The View only handles creation of view items and an interface to a ViewModel. The ViewModel implements the interface and handlss operations on the view and interaction with the Model. Sample code below:

Model

 public class MyModel{
    public String myString;
    public MyModel(String myString){
       this.myString = myString;
    }
}

View

public class MyActivity{

    public ViewManager delegate;

    public interface ViewManager{
        void registerTextView(TextView tvText);
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity);
        delegate = new ViewController(this);
        TextView tvText = (TextView) view.findViewById(R.id.tvText);
        delegate.registerTextView(tvText);
    }
}

ViewModel

 public class ViewController implements MyActivity.ViewManager{
     Context activity;
     TextView tvText;
     MyModel myModel;

     public ViewController(Context app_context){
        activity = app_context;
        myModel = new MyModel("Hello World");
     }

    @Override
    public registerTextView(TextView tvText){
        this.tvText = tvText;
        tvText.setText(myModel.myString);           
    }
 }

However, I have not seen this approach anywhere else online and am unable to find much information that supports it being a good design pattern for android. I also have a few questions such as :

Should you have a separate ViewModel for every fragment or just Activities?

Does this approach perform well on configuration change and Activity recreation with the extra overhead of another class? Can you cast the context to your activity to enable use of the fragmentManager?

How does this scale as code gets more complex?

Does anyone have experience using this design pattern with android or could anyone point me in the direction of some good study material before i start converting all my projects to MVVM???

Upvotes: 7

Views: 4362

Answers (4)

Deniz Babat
Deniz Babat

Reputation: 316

I refactored available code a bit to get some points such as how to bind.


1. User model class
package com.example.mvvm.model;

public class User {
    private String email;
    private String myPassword;

    public User(String email, String password) {
        this.email = email;
        this.myPassword = password;
    }

    public void setEmail(String email) {
        this.email = email;
    }
    public String getEmail() {
        return email;
    }

    public void setMyPassword(String myPassword) {
        this.myPassword = myPassword;
    }
    public String getMyPassword() {
        return myPassword;
    }
}
2. LoginViewModel class
package com.example.mvvm.viewmodels;
import android.text.TextUtils;
import android.util.Patterns;

import androidx.databinding.BaseObservable;
import androidx.databinding.Bindable;

import com.example.mvvm.BR;
import com.example.mvvm.model.User;

public class LoginViewModel extends BaseObservable {

    @Bindable
    private User user = new User("","");

    private String successMessage = "Login was successful";
    private String errorMessage = "Email or Password not valid";

    @Bindable
    private String toastMessage = null;

    public String getToastMessage() {
        return toastMessage;
    }
    private void setToastMessage(String toastMessage) {

        this.toastMessage = toastMessage;
        notifyPropertyChanged(BR.toastMessage);
        notifyPropertyChanged(BR.user);
    }

    public void setUserEmail(String email) {
        user.setEmail(email);
        notifyPropertyChanged(BR.user);
    }

    public void setUserPassword(String password) {
        user.setMyPassword(password);
        notifyPropertyChanged(BR.user);
    }

    @Bindable
    public User getUser() {
        return user;
    }

    public void onLoginClicked() {
        if (isInputDataValid())
            setToastMessage(successMessage);
        else
            setToastMessage(errorMessage);
    }

    public boolean isInputDataValid() {
        return !TextUtils.isEmpty(getUser().getEmail()) &&
           Patterns.EMAIL_ADDRESS.matcher(getUser().getEmail()).matches() &&
                getUser().getMyPassword().length() > 5;
    }
}
3. activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:bind="http://schemas.android.com/tools">


    <data>
        <variable
            name="viewModel"
            type="com.example.mvvm.viewmodels.LoginViewModel" />
    </data>


    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_margin="8dp"
            android:orientation="vertical">

            <EditText
                android:id="@+id/inEmail"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="Email"
                android:inputType="textEmailAddress"
                android:padding="8dp"
                android:text="@={viewModel.user.email}" />


            <EditText
                android:id="@+id/inPassword"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="Password"
                android:inputType="textPassword"
                android:padding="8dp"
                android:text="@={viewModel.user.myPassword}" />


            <Button
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="8dp"
                android:onClick="@{()-> viewModel.onLoginClicked()}"
                android:text="LOGIN"
                bind:user="@{viewModel.user}" />


        </LinearLayout>

    </ScrollView>

</layout>
4. MainActivity View class
package com.example.mvvm.views;

import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.BindingAdapter;
import androidx.databinding.DataBindingUtil;

import android.os.Bundle;
import android.view.View;
import android.widget.Toast;

import com.example.mvvm.R;
import com.example.mvvm.databinding.ActivityMainBinding;
import com.example.mvvm.model.User;
import com.example.mvvm.viewmodels.LoginViewModel;

import java.sql.Time;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityMainBinding activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        activityMainBinding.setViewModel(new LoginViewModel());
        activityMainBinding.executePendingBindings();
        activityMainBinding.setViewModel(new LoginViewModel());
        activityMainBinding.inEmail.setText("[email protected]");
        activityMainBinding.inPassword.setText("password");

    }

    @BindingAdapter({"user"})
    public static void runtest(View view, User user) {
        if (user.getEmail() != null)
            Toast.makeText(view.getContext(), user.getEmail(), Toast.LENGTH_SHORT).show();
    }
}

Upvotes: 0

Manas Chaudhari
Manas Chaudhari

Reputation: 276

I have been working on a library for building Android apps in MVVM pattern. You should find examples there.

https://github.com/manas-chaudhari/android-mvvm

Core ideas:

  • Each XML/View must have a ViewModel, though multiple XMLs can share a ViewModel
  • Each ViewModel should have a Model. Multiple ViewModels can share a Model
  • Use Data Binding to link ViewModel <-> View

Related Blog Post for architecture: https://manaschaudhari.com/blog/2016/08/19/rxjava-meets-data-binding-part-3

Upvotes: 1

Ramkailash
Ramkailash

Reputation: 1850

Android MVVM Design Pattern

enter image description here

The Data Binding Library offers both flexibility and broad compatibility — it's a support library, so you can use it with all Android platform versions back to Android 2.1

Build Environment

android {
    ....
    dataBinding {
        enabled = true
    }
}

You can follow this link step by step and apply databinding in your android projects.

Advance Guide go to developer page Link

Upvotes: 5

Cheng
Cheng

Reputation: 572

I will try to give my opinion. I think the sample code you gave didn't follow the core value of applying MVVM(or presentation model. MVVM is originated from presentation model) pattern. One of the major motive of the pattern is to make ViewModel(or Presentaion Model) pure POJO so that ViewModels allow maxmium testability. I have not read the book, but i recommend you to read Martin Fowler's original article about the pattern. I created some examples to demonstrate how to apply the pattern in Android development. If you are interested, you can have a look here - Album Sample, which is an android translation of Martin Fowler's original album example, and AndroidMVVM, a minimal demo app.

One way to apply the pattern is: View(Activity or fragment+layout), ViewModel, Model(business model: persistence layer, networking etc..). With this approach, to answer your question, i think one fragment maps to one ViewModel.

The pattern is to improve the design. When applied correctly, it will reduce the complexity not the other way around. Hope this helps.

Upvotes: 5

Related Questions