MVVM Architecture & LiveData, ViewModel, LifeCycle Components

Image for post
Image for post

MVC, MVP and MVVM are some of the Architecture patterns used by developers around the world. Which one to use, depends upon the requirement and how comfortable is one with the architecture pattern. However you should try to use one, it helps to keep the code segregated, modularised, readable, decoupled and easier to test.

Since the android development started Google never came up with their own architecture until recently. Google has introduced android architecture components which includes Life cycles, ViewModel, Room and LiveData.

Why am I here

This Article will provide brief of MVVM Architecture, LiveData, ViewModel and Lifecycle components.

Let’s get started

Before we start, I would recommend you to download a project from . This Application displays list of news in a Recycler view and on click of any news item, detailed news will be opened in a WebView. You can also refer screenshots from . As we go through the article, we will be using pieces of code from this project to understand architecture pattern and components.

Downloaded, Good to Go 👍🏻

So Finally there is an architecture(MVVM) which follows the . Each component does single and unique Job, which is why it is recommended to write as many component classes as many needed. Before moving to MVVM Let’s understand Android components.

We will start with LiveData

LiveData is an observable holder class. It is like any other observer which implements Observer class, but LiveData is also linked with component (activity/Fragment/Service) life cycle. In case a component is destroyed, it will automatically stop observing the changes and release unused allocated memory which would help in avoiding memory leaks.

We can wrap around an object to LiveData and start listening for data changes.

// setting value
public LiveData<News> getNews(String source) {
final MutableLiveData<News> data = new MutableLiveData<>();
getNews(source).enqueue(new Callback<News>() {
@Override
public void onResponse(Call<News> call, Response<News> response) {
data.setValue(response.body());
}

@Override
public void onFailure(Call<News> call, Throwable t) {
data.setValue(null);
}
});
return data;
}
//listening for change in <Object>. // this would be in ui layergetObservableProject().observe(this, new Observer<Object>() {
@Override
public void onChanged(@Nullable Object obj) {
// handle changes
}
});

In the Above Example News object is being fetched by an api call and is wrapped inside LiveData. UI layer observes LiveData object changes. So on change of object properties, UI would be changed automatically. Also, it will save us the trouble of handling cases such as what happens if user kills activity in the middle of API call since it will stop listening to the changes as soon as activity is destroyed.

ViewModel

If you use a ViewModel, you would not have to worry about saving the data on configuration changes. In general, on configuration change activity is recreated and so does every other data retrieval. It can be an API call, db call or any other data related operation. ViewModel saves us all this trouble and keep the data alive until component is killed.

Let’s say we have an activity which displays result based upon searched query. Activity gets search query text from another component and then makes an API call to display results. In this case if user rotates device, there would be repetitive API call. If a ViewModel is used for any such cases then data would be saved automatically.

ViewModel objects are attached to component life cycle and only. In case of Activity it would be in onDestroy() and in case of Fragment it would be in onDetach().

// View model for News Objectpublic class NewsViewModel extends AndroidViewModel {
private final LiveData<News> newsLiveData;

public ObservableField<News> news = new ObservableField<>();

public NewsViewModel(@NonNull Application application) {
super(application);
// a differnt source can be passed, here i am passing techcrunch
newsLiveData = NewsRepository.getInstance().getNews("techcrunch");
}

public LiveData<News> getObservableProject() {
return newsLiveData;
}
}
// accessing NewsModel Objectfinal NewsViewModel viewModel = ViewModelProviders.of(this, factory)
.get(NewsViewModel.class);
viewModel.getObservableProject().observe(this, new Observer<News>() {
@Override
public void onChanged(@Nullable News news) {
//
}
});

In the example, News object is wrapped inside NewsViewModel and same is used for observing data inside fragment.

, that might lead memory leaks since ViewModel does not get destroyed on configuration changes. However ViewModel won’t be retained on back press, if application is removed from recent or if system kills the activity. In such cases onSaveInstance method has to be implemented to retain the needed data.

It can be done something like this.

public class SharedViewModel extends ViewModel {
private final MutableLiveData<Item> selected = new MutableLiveData<Item>();

public void select(Item item) {
selected.setValue(item);
}

public LiveData<Item> getSelected() {
return selected;
}
}

public class MasterFragment extends Fragment {
private SharedViewModel model;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
itemSelector.setOnClickListener(item -> {
model.select(item);
});
}
}

public class DetailFragment extends Fragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
model.getSelected().observe(this, { item ->
// Update the UI.
});
}
}

Off course, You can do it using interfaces but that is a cumbersome way to handle communication. If you use ViewModel, Activity and Fragment would not have any clue of communication. They will be communicating through SharedViewModel. You can read more about it from here.

Handling Life Cycle

This Architecture components comes with LifeCycle Observer. As name suggests you can create an observer which will bind to a component’s lifecycle.

In the below example SomeObserver is implementing LifecycleObserver class. SomeObserver is bound to an activity so it will follow activity’s lifecycle. As Activity’s onResume gets called, onResume of SomeObserver will also be called since it is linked to ON_RESUME event of Activity.

// registering observer in activity class
getLifecycle().addObserver(new SomeObserver());
// implementing LifecycleObserver class
public class
SomeObserver implements LifecycleObserver {

final String TAG = this.getClass().getSimpleName().toString();

@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
public void onResume() {
Log.d(TAG, "onResume called");
}

@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
public void onPause() {
Log.d(TAG, "onPause called");
}
}

: You want to fetch user’s fine location only if activity is visible. if not then coarse location. For this, In the observer class for onResume and onPause method you can switch the location update source from coarse to fine or vice-versa.

in Support Library 26.1.0 and later. Before that you would have to extend LifeCycleFragment or LifeCycleActivity for fragment and activity correspondingly.

Also, You can make your own class Lifecycle Owner by implementing LifeCycleOwner class and mark the methods which you want to expose using LifecycleRegistry.

public class MyActivity extends Activity implements LifecycleOwner {
private LifecycleRegistry mLifecycleRegistry;

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

mLifecycleRegistry = new LifecycleRegistry(this);
mLifecycleRegistry.markState(Lifecycle.State.CREATED);
}

@Override
public void onStart() {
super.onStart();
mLifecycleRegistry.markState(Lifecycle.State.STARTED);
}

@NonNull
@Override
public Lifecycle getLifecycle() {
return mLifecycleRegistry;
}
}

We are done with Architecture components.

Model-View-ViewModel

DataModel

DataModel contains the data from different sources, can be API or can be from databaseItMost of the times datamodel contains API or DataBase Logic. DataModel fetches the data from required sources and present it to ViewModel. This layer also handles business logic so that latest and required data is exposed to the ViewModel.

ViewModel

This ViewModel is different from what we discussed above. This is term defined for MVVM Architecture.

ViewModel interacts with Data Model by an observable and It exposes only needed data for View Layer by an observable. Basically ViewModel does the same job for View, what Model does for ViewModel.

ViewModel prepares data for UI layer and keep it if needed, Any action on UI will be handled by ViewModel and any data changes will be observed by UI in View Layer. This helps to keep the UI and data Logic separated.

. If we need 2 properties of an object, we will fetch those 2 properties, combine them into single object and then expose for View.

View

View is the UI interface , It can be an activity, fragment or a custom view. ViewModel prepares data for View, Binding of ViewModel to View is done in layout XML This is called Data binding of Layout Files.

To make the XML bindable, layout tag needs to be included in the XML. By Default a Binding class would be generated which will have references of all views and properties. if name of layout is activity_main, binding file would be ActivityMainBinding.

In layout XML file we can also include data object, with the help of which data can be populated to the views.

// ViewModel reference in XML
<data>

<variable
name="article"
type="com.gauravgoyal.mvvm_with_testing.service.model.Article"
/>

</data>
//using Viewmodel to display data<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/app_name"
android:text="@{article.title}"
android:textSize="@dimen/news_text"
android:textStyle="bold"
/>

In the above example we have declared Article object and we are using this object to set the title in textview.

However some times we need to write custom implementation for a property, Let’s say we are getting a Date as String from data object and want to format this date to another format, For such cases we can write our own custom methods annotating with BindingAdapter and passing the property name.

//xml
<TextView
android:id="@+id/publishedAt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:dateText="@{article.publishedAt}"
android:textSize="@dimen/news_text"
/>
//handling dateText property@BindingAdapter("dateText")
public static void convertToDate(TextView view, String date) {
view.setText(DateUtils.Companion.convertToDateString(date));
}

In the example i am using dateText property. Android does not know this property so we have to write a method which represents this property, now anywhere in the application you can use this tag and on run time it will call the BindingAdapter annotated method.

You can pass multiple properties for BindingAdapter method, making them optional or mandatory. You can read more about binding from here.

Test Project

Now let’s try to put all these pieces together and find out what is what in the sample project.

  1. Article,News,Source are the POJO Models AndNewsRepository is class which prepares data for NewsViewModel. NewsRepository takes care of API call and returns a Observable (LiveData) object. With the help of observable we can observe the data anywhere in the application. DataModel does not care who is observing the data, it’s job is to expose relevant data.
  2. NewsViewModel is layer which prepares data for UI layer. ViewModel is observing data which is exposed by NewsRepository and it exposes needed data for UI which will be observed by View Layer. Usually action listeners are part of ViewModel.
  3. ArticleListFragment is layer and gets data from NewsViewModel by observing LiveData Object. Once changes are triggered in data object, ui will inflate those changes. news_list_item is XML for News Item and bound to Article object.

Conclusion

It’s good to have an architecture in place, helps to keep code modularised , components decoupled and make testing easier. But as someone has said “you would not know until you try it”.

Going forward in the next post I will talk about Testing frameworks and how these patterns and components can help write unit and instrumental test cases.

Lastly, Thank you for reading the Article, Any Questions and suggestions are most welcome, Also if you have got anything out of the article, Please clap and follow. See you soon. Happy Coding 😃

An Android enthusiast & a peRFect Tennis fan!

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store