# Android Architecture: MVVM Pattern ###### tags: `Android Architecture` [TOC] ## What is MVVM? ![](https://i.imgur.com/bmhdeVh.png =500x350) MVVM stands for Model-View-ViewModel. ### Each Part **Model:** It is the data layer, responsible for managing the business logic and handling network or database API. **View:** It is the UI showing the data obtained from model. This one will keep observing the property for change, and update itself when the property changes. **ViewModel:** The ViewModel itself will have observables that will be updated from time to time. This one will **NOT** tell the view that there was a change in observables, because views keep observing the properties and do actions when there is a change. ### How it works? 1. ViewModel class implementing ViewModel will have observables attributes, which are updated in ViewModel itself after retrieving informationg from Model. 2. View will be keeping an eye on ViewModel Attributes to see whether there is a change on it or not. 3. If there is a change it will update the UI by the view itself. ### Other things used in this example #### 1. Gradle App ```java== def lifeCycleExtensionVersion = '1.1.1' //MVVM needs: (Google Library) implementation "android.arch.lifecycle:extensions:$lifeCycleExtensionVersion" ``` ## MVVM Coding Parts ### 1. Model #### Information Main Structure: ```java== import com.google.gson.annotations.SerializedName; public class Country { @SerializedName("name") public String countryName; @SerializedName("capital") public String capitalName; @SerializedName("region") public String regionName; } ``` #### Interface used in >Why is it ++*@GET("all")*++? That is because the url was: https://restcountries.eu/rest/v2/++**all**++[color=red] ``` java== public interface CountriesApi { @GET("all") Single<List<Country>> getCountries(); } ``` #### Retrieve information from API: CountriesService Class used Retrofit library to get information from the API. ``` java== public class CountriesService { public static final String BASE_URL = "https://restcountries.eu/rest/v2/"; private CountriesApi api; public CountriesService() { Retrofit retrofit = new Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build(); api = retrofit.create(CountriesApi.class); } public Single<List<Country>> getCountries(){ return api.getCountries(); } } ``` ### 2. View :::info **Note:** MVVMViewModel has extended ViewModel, so when we have to instantiate and give it a value in our view, we use: ++ViewModelProviders.of(Context).get(Class)++ **Advantage:** Provides lifecycle management; and creates parameters ands destroy them when they not needed anymore. ::: ```java== import android.arch.lifecycle.Observer; import android.arch.lifecycle.ViewModel; import android.arch.lifecycle.ViewModelProviders; import android.content.Context; import android.content.Intent; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.View; import android.widget.Button; import android.widget.ProgressBar; import android.widget.Toast; import java.util.ArrayList; public class MVVMActivity extends AppCompatActivity { private RecyclerView rv_country; private MVVMAdapter mvvmAdapter; private MVVMViewModel viewModel; private Button btn_retry; private ProgressBar pb_bar; public interface onClickI{ void onClick(int i); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_mvvm); setTitle("MVVM"); this.rv_country = findViewById(R.id.rv_country); this.btn_retry = findViewById(R.id.btn_retry); this.pb_bar = findViewById(R.id.pb_bar); //It creates all lifecycle management viewModel = ViewModelProviders.of(this).get(MVVMViewModel.class); this.mvvmAdapter = new MVVMAdapter(new onClickI() { @Override public void onClick(int i) { Toast.makeText(MVVMActivity.this, "" + i, Toast.LENGTH_SHORT).show(); } }); RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false); this.rv_country.setAdapter(mvvmAdapter); this.rv_country.setLayoutManager(layoutManager); observeViewModel(); } //This will help to keep a close check on the observables in the viewModel private void observeViewModel(){ viewModel.getCountries().observe(this, new Observer<ArrayList<Country>>() { @Override public void onChanged(@Nullable ArrayList<Country> countries) { if(countries != null){ mvvmAdapter.setCountryList(countries); btn_retry.setVisibility(View.GONE); pb_bar.setVisibility(View.GONE); rv_country.setVisibility(View.VISIBLE); } else { rv_country.setVisibility(View.GONE); } } }); viewModel.getError().observe(this, new Observer<Boolean>() { @Override public void onChanged(@Nullable Boolean aBoolean) { if(aBoolean){ Toast.makeText(MVVMActivity.this, R.string.error_msg, Toast.LENGTH_SHORT).show(); btn_retry.setVisibility(View.VISIBLE); } else { btn_retry.setVisibility(View.GONE); } } }); } public void onRetry(View view) { viewModel.onRetry(); this.btn_retry.setVisibility(View.GONE); this.pb_bar.setVisibility(View.VISIBLE); this.rv_country.setVisibility(View.GONE); } public static Intent getIntent(Context context){ return new Intent(context, MVVMActivity.class); } } ``` ### 3. ViewModel First We need our ViewModel class to implement ViewModel from android.arch.lifecycle ![](https://i.imgur.com/whZff2U.png =500x30) :::info **Note:** ViewModel will have observables which are being observed by Views, so as to update itself. So we do not need to notify views and viewModel just needs to update its own parameters. ::: ```java== import android.arch.lifecycle.LiveData; import android.arch.lifecycle.MutableLiveData; import android.arch.lifecycle.ViewModel; import android.util.Log; import java.util.ArrayList; import java.util.List; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.observers.DisposableSingleObserver; import io.reactivex.schedulers.Schedulers; import studio.zc.androidarchitecture.model.CountriesService; import studio.zc.androidarchitecture.model.Country; public class MVVMViewModel extends ViewModel{ private CountriesService service; //Observables (only updated within class) private final MutableLiveData<ArrayList<Country>> countries = new MutableLiveData<>(); private final MutableLiveData<Boolean> countryError = new MutableLiveData<>(); public MVVMViewModel() { service = new CountriesService(); fetchCountries(); } private void fetchCountries(){ service.getCountries() .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .subscribeWith(new DisposableSingleObserver<List<Country>>() { @Override public void onSuccess(List<Country> value) { ArrayList<Country> rv_list = new ArrayList<>(); rv_list.addAll(value); //MVVVM attribute: It updates its local variables and not view countries.setValue(rv_list); countryError.setValue(false); } @Override public void onError(Throwable e) { countryError.setValue(true); } }); } public LiveData<ArrayList<Country>> getCountries(){ return countries; } public LiveData<Boolean> getError(){ return countryError; } public void onRetry(){ fetchCountries(); } } ``` ## Advantange & Disadvantages **Advantage:** >**Main advantage?** It well separates UI and non UI parts meaning that it will be easier to do Unit Testing. [color=pink] **Disadvantage:** * It is not suited for those simple projects.