There were some interesting stuff at this year’s Google I/O 17. One such session was Architecture Components - Introduction by Yigit Boyar and Luke Bergstorm. The announcement included new architecture components library.
What’s Android architecture components
A new collection of libraries that help you design robust, testable, and maintainable apps. Start with classes for managing your >UI component lifecycle and handling data persistence.
The architecture components come with lot of new components such as
LiveData, ViewModel, LifecycleObserver and LifecycleOwner and
Room Persistence library. Together these components would help developers follow the right architecture pattern with proper separation of responsibility for each layer (more on that later) and help survive configuration changes, avoid memory leaks and updating the UI in case of changes in our data.
LifecycleOwner - Interface for components having Lifecycle (such as Activities and Fragments).
LifecycleObserver - LifecycleObserver subscribes to LifecycleOwner’s lifecycle state changes and are self sufficient. It does not have to rely on Activity or fragment’s
onStop()lifecycle callback to initialise and stop it.
LiveData - It is an observable data holder, it notifies the observers in case of data changes. It is also lifecycle aware component i.e. it respects the lifecycle state of the
LifecycleOwner(Activities or Fragments). This helps preventing memory leaks and other issues.
ViewModel- Objects that provide data for the UI components. They does not have the references to the view and are unaffected by the lifecycle of LifecycleOwner.
Room - SQLite based object mapping library. Room abstracts the underlying layer of implementation details like creating and managing database and tables. Room uses annotations to generate code at compile time. Furthermore Room also supports LiveData and Rx Java 2 Flowables
MVVM Pattern using Architecture components
To demonstrate the MVVM architecture pattern, let’s create a simple android app to fetch the issues of any Github repository. In this post We will be mostly be covering ViewModel and LiveData components.
Picture from: Android developers site
We will be having the following components layers:
View - This layer contains UI components and is responsible for view related code such as initialising child views, displaying progress bar, receiving input from user and handling animations, etc. Example: Activities and Fragments.
ViewModel - ViewModels provide data to the UI components. In our case views will be using LiveData to observe the data changes in ViewModel.
Repository - Repositories abstract the underlying implementation of Data sources that an app can use cache and fetch the data. This abstraction is helpful in two ways: 1) the code is not dependent on the concrete implementation of data store, and 2) Because of previous point, we can swap the implementation of datastore at any time such as for testing.
Data Service - This layer contains the actual code that caches (by using SQLite for example) or fetches data (like by fetching data from backend api’s using retrofit).
Enough of talk. Let’s Code
Open project level
build.gradle file and add the following:
Add the retrofit 2 and architecture components dependencies to app level
Next we will create the entities.
Enter the following URL in your browser https://api.github.com/repos/square/retrofit/issues to get a list of JSON objects. Grab one one JSON object and head to the following site: jsonschema2pojo and paste the copies JSON object in the text area. Select source type as
JSON and Annotation Style as
GSON since we will be using GSON converter to convert JSON to entity.
Now click preview and grab the generated code. Create 2 entities
Next, lets create another entity
We will use
ApiResponse to communicate data from Repository to ViewModel and ultimately to Activity.
So if we get any error while fetching data from the remote api, we will set Error in the
ApiResponse, else we will set the list of
Issue objects into it.
Next let’s create Retrofit Service interface:
Next, we’ll create Repository. First lets create
Now we’ll create
IssueRepositoryImpl which implements
IssueRepository interface. As discussed above Repositories are used to abstract the communication of rest of the code to the Data sources (such as Database or API calls). In our case
IssueRepositoryImpl will use
GithubApiService to fetch data from Github API and return the value as a
getIssues() method we create a
MutableLiveData from the data obtained from retrofit.
MutableLiveData is the subclass of LiveData that has
setValue(T) method that can be used to modify the value it holds.
Next let’s create our ViewModel class named
ListIssuesViewModel which extends
ViewModel abstract class:
ListIssuesViewModel will fetch the data requested by the UI from the IssueRepository. It has
mApiResponse which is observed by the UI.
MediatorLiveData is a subclass of
MutableLiveData which allows us to observe one or more LiveData (
LiveData from Repository’s
getIssues() method in our case) and propagate the changes to it own observers (Activity in our case).
In case you want to have a ViewModel class with non-empty constructor, you have to create a Factory class which would create instance of you ViewModel and that Factory class has to implement
If you want reference to Application context in your View Model class, you can use AndroidViewModel class instead of
Finally, create an activity which extends
AppCompatActivity class (Since
LifecycleActivity has been deprecated as of version architecture components
1.0.0-alpha9–1 and both
AppCompatActivity and Support Fragment now implement the
LifecycleOwner interface) with EditText and Recycler View.
onCreate() we will initialise the ViewModel, observe the
mApiResponse and take appropriate action to display the view.
If user initiates a new search query, we will call
viewModel. loadIssues(@NonNull String user, String repo) method with appropriate parameters.
So we now have an app which uses android recommended architecture pattern by using MVVM, LiveData and Repository. Our app also persists data across configuration changes such as screen rotations.
Android developer’s Guide to app architecture suggests using Dagger 2 library for dependency injection and Room ORM for data persistence.