In this article, we will talk about Model View View Model(MVVM)
Pre-requisites: An idea about Android OS, UI/UX would make this a more interesting read.
Android is a mobile operating system developed by Google. It is based on a modified version of the Linux kernel and other open source software, and is designed primarily for touchscreen mobile devices such as smartphones and tablets. The mobile app design constitutes the UI (user-interface) which is responsible for taking user inputs in various ways like touch, camera image, shake of the device, etc and gives output in some form. UI is very essential for any app and hence, better UI design is desirable. The UX stands for User Experience and it is always desirable to have a better user experience for any android app. An example could be performing a task using an app in just 2 screen touches without having to learn much about how to use the app from user’s point of view. Now coming to the actual topic view model in android.
The ViewModel class is designed to store and manage UI-related data in a lifecycle conscious way. The ViewModel class allows data to survive configuration changes such as screen rotations. The Android framework manages the lifecycles of UI controllers, such as activities and fragments. [1]
[Note: An Android Activity is one screen of the Android app’s user interface. The fragment is a part of an activity hosted by an activity. The lifecycle of an activity or a fragment is the one from its creation to till its destroyed. In between these two, lots of operations may happen like pause, resume, etc. based on some events that may occur.]
It is up to the framework to decide whether to destroy or re-create a UI controller in response to specific user actions that are entirely out of your control (Ex: screen rotation).
i.e.,
// code (for whomever interested in android development!) @Override public void onCreate(Bundle savedInstanceState) { // method that is called first in the activity or fragment super.onCreate(savedInstanceState); bind(); } public void bind() { binding = DataBindingUtil.setContentView(this, getLayoutId()); this.viewModel = viewModel == null ? onCreate() : viewModel; // here the decision is made whether to re-create or just use the view model by checking // if the viewModel is already used or its called for the first time after it got destroyed previously binding.setVariable(getVariable(), viewModel); binding.executePendingBindings(); }
If a UI controller is recreated or destroyed, then the transient UI-related data that was then held by the activity/fragment is lost. For example, your Android app may include a list of food recipes/places in one of its activities/fragments. When the activity is re-created for change of configuration, the new activity has to re-fetch the list of food recipes maybe from database or server (which is a waste of time and data).
For small amounts of data, an activity can use the onSaveInstanceState() method to restore the required data from the bundle in onCreate(). But, do you think this can work for a list of data like the one which we discussed earlier (food-recipes) or maybe for a series of bitmaps? No, right?
[Note: onCreate() method is a method that gets called first when an activity is created, re-created or resumed and is responsible for creating that activity. The savedInstanceState is a reference to a Bundle object that is passed into the onCreate method of every Android Activity. Activities have the ability, under special circumstances, to restore themselves to a previous state using the data stored in this bundle. Bundle is used to pass data between Activities.]
One thing I want you all to know here is if your app contains multiple screens, each one of them will need a corresponding activity, i.e, a java class and a resource file (XML) that holds the UI part. Hence to pass data from one activity to another in your app, data need to be transferred using these bundles.
For example, just assume that you have created a calculator app and the soul purpose of it is to take two numbers as input and perform certain arithmetic operations on them. In case, the user accidentally or on purpose changes the screen orientation, in order to recover the numbers which were given as input, we can save them in onSaveInstanceState() and retrieve it which apparently looks like those numbers were kept intact. Remember if you forget to do this then the input numbers will be lost and will have to be re-entered. But can you do this for thousands of images or a list of food recipes. Manually it is a very tedious job and hence its better to use a view model in such cases.
Another problem that arises is that UI controllers frequently need to make asynchronous calls that may take some time to return (Ex: loading an image from a given URL using Glide or Picasso). That can test the user’s patience!
These calls need to be managed by the UI-controller, which must also ensure the system cleans them up after it’s destroyed to avoid potential memory leaks which require a lot of maintenance [2]. In case the object is re-created, it’s a waste of resources since the object may have to reissue calls that were already made. UI-controllers are nothing but activities/fragments that are responsible for communication between the user and the Android device.
This can take place by requesting app permissions to the user, a button press, change in orientation, etc. Loading the UI of activity on the screen and the data pertaining to the same into its corresponding java class are some of its other responsibilities. In case we end up assigning an excessive task to these UI controllers, we will end up having a java class that’s trying to handle quite a lot of app’s work by itself instead of distributing work to other classes. The consequence of this is difficult testing of that component. It’s easier and more efficient to separate out the UI related tasks, backend tasks, etc from one another so that it gets easier later to fix a bug while testing [2]. In this way the code quality, readability, and re-usability increases, which is a good practice for an Android developer.
Let me talk about one such architecture.
Model-view-ViewModel
Model–view–viewmodel (MVVM) is a software architectural pattern.
MVVM facilitates a separation of development of the graphical user interface – be it via a markup language or GUI code (XML in case of android development) – from the development of the business or back-end logic (the data model)[3]. It is responsible for converting the data objects from the model in such a way that objects are easily managed and presented. Some back-end logic that is associated with the view is also needed to be taken care of by the view model. Hence its more than a model.
Some terms that you need to know:
[Model, View, View model, Binder]
I would recommend you this blog to get explicit knowledge about these terms if you haven’t come across them.
The thing I like most about it is the compartmentalization of the various components in a project.
-The UI components separated from the back-end logic
-The business/back-end logic need not be mixed with the database operations
-It’s easy to read the code since every component has a specific place where it’s situated.
-And if done correctly, you need not worry much when it comes to lifecycle events (ex: screen rotations) which are not something can be controlled.
Repositories
Repository classes handle data operations. Creating a repository helps in providing distinctions between how an application retrieves data and how it makes use of it.
Repositories know the source to fetch the data (ex: Firebase Storage) and accordingly, what API calls that are needed to keep the data updated (ex: Weather report data). You can consider repositories to be mediators between different data sources, such as persistent models, web services, and caches.
In essence, we are assigning specific purpose to a particular component in a project. In this way, it is easier to jump into that component to code further without causing any inconvenience in case of software to debug or maintenance. There are certain limitations or boundaries associated with what a component can perform, and they are deliberately made to ease out the task of that component, hence avoiding handing over excessive responsibility to any element by following this approach.
References:
[1] – https://medium.com/@amtechnovation/android-architecture-component-mvvm-part-1-a2e7cff07a76
[2] – https://developer.android.com/topic/libraries/architecture/viewmodel
[3] – https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel