Modern Android architecture by using View Model
| A bridge of elegant design in T.O. |
In 2018, Google released Android Jetpack, a suite of code library, tools and guidelines that included the APIs of architecture components. The APIs are designed to help building common tasks faster and more reliable, and at the same time conforming to the architectural guidelines recommended.
An Android app consists of one or more user-interface controllers. There are already a lot of codes in this area. It is not a good approach to have the codes, for example, written for managing Web Service and database I/O included in the U.I. Controller component. Good design puts codes in the right places to reduce complexity, and enables collaboration among teams.
A U.I. Controller consists of one or more Activities. Each Activity uses zero, one or more Fragments. Each Fragment uses other classes. While a U.I. defines how the data is displayed and collected on the screen, a Repository manages the incoming and outgoing data. U.I. Controller and Repository are the two areas that should be separated but bridged for communication. The liaison role in between is a component called View Model. A View Model often consists of Live Data that holds the incoming values from a Web Service. The Live Data is observed from within the U.I. Controller that when new data comes in, the U.I. will get automatically updated. Such automation is enabled by Data Binding.
![]() |
| Architecture components interpreted by Calmalgo |
U.I.
A user-interface consists of XML layout files. Most (if not all) the fields, buttons and pictures etc. are defined in those files.
U.I. Controller
The U.I. Controller component consists of one or more Activities. An Android app starts from the main activity class that instantiates the first U.I. on screen and subsequently many others. All the U.I.-related classes starting from the main activity are considered part of the U.I. Controller component.
View Model
What makes this architecture effective is primarily the use of View Model. It doesn't only separate the U.I. Controller and the Repository, but also allows different fragments within the U.I. Controller component communicate to one another. Further, a View Model can memorize the states on screen, and that will simplify the typical lifecycle issues in the U.I. Controller. For example, when a user rotates the screen from portrait to landscape, that will get the corresponding Activity destroyed and re-created, leading to loss of data on screen if the screen-rotation is not handled properly. By using View Model, there is less coding required to get the states on screen memorized. While the Activity is destroyed, the View Model survives and memorizes the states.
The View Model does not know the details of the U.I. Controller. On the contrary, the U.I. Controller knows the details of the View Model and uses them. When the U.I. Controller needs data for display, it gets the data from the View Model; and when it receives data from the user, it sets the data to the View Model. The U.I. Controller does not know how the Repository uses the Web Service and database I/O. It only knows telling the View Model to do so.
Live Data, Observer, Data Binding
Live Data is a data holder that is observable. Imagine Live Data holds a list of earthquakes, stock quotes, vehicle engine states, customer service center states, or any type of data that requires refreshing frequently. The Observer monitors the Live Data, and has the ability to notify other objects when a change of the data occurs. Once the new data is received, the Observer triggers the action of Data Binding to display the data.
Data Binding allows data in a View Model to be mapped directly to specific views within a XML layout. This helps a lot on reducing code to keep the U.I. up-to-date.
Repository that uses Web Service and Database
Many apps rely on a Web Service to receive data through the Internet. For mobile devices, from time to time, a reliable network connection may not be available. To provide a good user experience, having the data stored in a local database is preferable. When the user brings up the app in an area without mobile network or WiFi, the app instead of showing all the fields on screen blank, it should show the last data stored locally. To enable developers to build this function, the Android Jetpack Library provides the Room database API. The SQL statements are written as annotations in a data access class. When new data is received from the Web Service, the functions that update the database will be called to action.
----------
One-way Binding
The architecture described above recommends an Observer within the U.I. Controller to observe the changes of Live Data. That is known as one-way binding of the incoming data, and it is for the purpose of updating the U.I. Now consider a different scenario that the U.I. needs to collect data from the human user. The user-input that changes the values of the U.I. will not update the stored values unless there is additional coding in the U.I. Controller to handle that. For the scenario that there are quite many input fields on the U.I that will change the stored values, two-way binding will be effective.
Two-way Binding
A two-way binding allows the U.I. directly calls to one or more functions in the View Model. Calling the functions will update the internal state of the View Model, and execute other necessary steps. Putting the Repository aside, let's look at the diagram of two-way binding of handling the data collection from the U.I.
![]() |
| Two-way binding interpreted by Calmalgo |
Note that using the Observer API to monitor the incoming data from the Repository is not necessary in this case. When the app launches, the default states in the View Model is binding to the U.I. When there is user-input collected from the U.I., an instruction coded in the U.I. will call to a function in the View Model.
Depending on the purpose of the app, two-way binding is not necessarily better. The developer will consider which method of binding is suitable. One-way binding can be involved with automatic scheduling in the background and notification push. If the app is involved with both refresh of incoming data and collection of user-input, the integrated use of one-way binding (with Observer on some Live Data) and two-way binding (without Observer on some other Live Data) is possible.


Comments
Post a Comment