During the Google I/O event, we were introduced to the architecture components including LiveData and ViewModel to facilitate the development of Android applications with the MVVM pattern paradigm.
Definition of MVVM
If you are already familiar with MVVM, feel free to skip this section, so you don't waste your time.
MVVM stands for Model View ViewModel, which is one of the architectural design patterns or new paradigms to manage the concentration of Android application development, allowing developers to separate the User Interface from Business Logic (back-end). MVVM is an evolution of the legendary MVC pattern architecture, adhering to the same principles, namely;
Keeping UI code simple and free of app logic in order to make it easier to manage
MVVM has the following main layers.
1. Model
The model represents the application's data and business logic. One recommended implementation strategy of this layer, is to expose its data via observables to be completely separated from ViewModel
or Observer
otherwise.
2. ViewModel
ViewModel
interact with Model
and also prepare observables that can be observed by View
. Optionally, ViewModel
it can provide hooks/bindings for View
passing events to Model
. One important strategy of implementing this layer is to separate it from View
, i.e. ViewModel
it should not know about the views it interacts with.
3. View
Finally, the role of the view in this pattern is to observe or subscribe to observations ViewModel
, in order to get the data that the UI elements need.
The following diagram shows the MVVM components and basic interactions.
As mentioned above, LiveData
is one of the newly introduced architectural components. LiveData
responsible for carrying observed data. It allows application components to observe changes in object data LiveData
without creating explicit and rigid dependency paths between them. It completely decouples object producersLiveData
from object consumersLiveData
.
In addition, there are also great benefits of LiveData
, namely respecting the main lifecycle of your application (Activity, Fragment, Service) and handling object lifecycle management and ensuring that objects LiveData
do not leak.
Google documentation states that if you are already using a library like Rx or Agera, then you can continue with it as a replacement for LiveData. But in this case, you are responsible for managing object allocation & de-allocation in each components lifecycle.
Because LiveData
it respects the Android Lifecycle, it LiveData
will not call the observer callback unless the LiveData host (Activity or Fragment) is in an active state ( onStart()
). Also, LiveData
it will automatically remove the observer when its host is onDestroy()
.
ViewModel
ViewModel
also arguably one of the newly introduced architectural components, which is responsible for preparing data for UI/View.
ViewModel
provides a special class on the MVVM layer, because it ViewModel
(and its derived classes AndroidViewModel
) automatically extends the ability to persist data during configuration changes, for example when a screen rotates.
The following diagram shows the life cycle of a ViewModel component.
Now, let’s get to the most interesting part, let’s put the above puzzle together into a sample application. The first screen shown below displays a list of Google GitHub projects with some brief information like title, programming language, etc.
And when those items are clicked it will display details like this.
The following shows the project structure of this sample Application.
Meanwhile, this interaction diagram shows the application scenario of fetching a Google GitHub project.
The repository module is responsible for handling data operations. In addition, it also provides a clean and clear API, whether it is RestFul for external resource access, or room for internal resource access.
Now, let's explain these layers from bottom to top, starting with Model, ViewModel, and finally View to take a GitHub project scenario.
Sample App Model Layer
Let's start with the business logic layer, we have two model objects.
- Projects , contains GitHub project information such as id, name, description, creation date,… etc.
- Users , contains user information of the GitHub project owner.
To interact with the GitHub RESTful API, I use the beloved library Retrofit 2, here's how I define it in code.
interface GitHubService {
String HTTPS_API_GITHUB_URL = "https://api.github.com/";
@GET("users/{user}/repos")
Call<List<Project>> getProjectList(@Path("user") String user);
@GET("/repos/{user}/{reponame}")
Call<Project> getProjectDetails(@Path("user") String user, @Path("reponame") String projectName);
}
To facilitate the Job ViewModel
, a class ProjectRepository
is created to interact with the GitHub service and eventually provide an object LiveData
for ViewModel
. This will also be used later to orchestrate service calls. The following code snippet shows the API implementation getProjectList()
.
public class ProjectRepository {
private GitHubService gitHubService;
//…
public LiveData<List<Project>> getProjectList(String userId) {
final MutableLiveData<List<Project>> data = new MutableLiveData<>();
gitHubService.getProjectList(userId).enqueue(new Callback<List<Project>>() {
@Override
public void onResponse(Call<List<Project>> call, Response<List<Project>> response) {
data.setValue(response.body());
}
// Error handling will be explained in the next article …
});
return data;
}
// …
}
ProjectRepository
is the data provider for ViewModel
, which is getProjectList()
tasked with aggregating responses into Objects LiveData
.
To make it easier to understand this article, we have removed the error handling code, and we plan to review it in a separate article.
Sample App ViewModel Layer
Because the Repository API consumption process is done as a background task, this is the reason why getProjectList()
it needs to be created in ViewModel
, it calls LiveData
, the goal is so that we can consume the data from the background task transformation to the main thread (UI). The following code snippet shows the ProjectListViewModel
.
public class ProjectListViewModel extends AndroidViewModel {
private final LiveData<List<Project>> projectListObservable;
public ProjectListViewModel(Application application) {
super(application);
// If any transformation is needed, this can be simply done by Transformations class ...
projectListObservable = ProjectRepository.getInstance().getProjectList("Google");
}
/**
* Expose the LiveData Projects query so the UI can observe it.
*/
public LiveData<List<Project>> getProjectListObservable() {
return projectListObservable;
}
}
In real world cases, a transformation may be required before passing the data to View Observer
. You can use the Transformation class as shown in THIS documentation .
Since Activity
and Fragment
is considered the owner of the lifecycle, then in real case, you can initialize the observer with this
(if it is done in Activity), and viewLifeCycleOwner
if it is done in Fragment
.
public class ProjectListFragment extends LifecycleFragment {
private ProjectAdapter projectAdapter;
//…
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
final ProjectListViewModel viewModel =
ViewModelProviders.of(this).get(ProjectListViewModel.class);
observeViewModel(viewModel);
}
private void observeViewModel(ProjectListViewModel viewModel) {
// Update the list when the data changes
viewModel.getProjectListObservable().observe(this, new Observer<List<Project>>() {
@Override
public void onChanged(@Nullable List<Project> projects) {
if (projects != null) {
//…
projectAdapter.setProjectList(projects);
}
}
});
}
//…
}
This is an explanation of one end-to-end project scenario, you can find the full project HERE .