MVVM (Model View ViewModel) + Kotlin + Google Jetpack

MVVM (Model View ViewModel) + Kotlin + Google JetpackAnkit BishtBlockedUnblockFollowFollowingMay 2MVVM + Kotlin + JetpackToday, we are going to learn how we can develop an Android app using MVVM architecture.

This post will be based on the Android architecture components which are covered in Google’s Android Jetpack which was officially announced by Google in I/O meet 2017, click here to learn more about the android jetpack.

ANDROID JETPACK is a collection of components that are required to develop a great Android app.

It provides common infrastructure code so that you can focus on what makes your app unique.

Key features going to cover:- MVVM (KOTLIN)- ViewModel- LiveData- Navigation Architecture component- Data BindingPlease check this project for reference, I will be using this git project as an example.

This project consists of two screens, the first one displays all the trending git repositories and the second one will display the detail view of each repository.

LET’S GET STARTEDMVVM stands for Model View ViewModel.

This is one of the best design patterns that can be used to develop an Android App but what makes MVVM more powerful are the components that we are going to use today from Android Jetpack.

Kotlin + MVVM + Databinding = Easy maintainable and efficient codeIn MVVM,Model — contains all the data classes, database classes, API and repositoryView — is the UI part that represents the current state of information that is visible to the user.

ViewModel — it contains the data required in the View and translates the data which is stored in Model which then can be present inside a View.

ViewModel and View are connected through Databinding and the observable Livedata.

Key benefits,It separates the business logic with the presentation logicThe View is not aware of all the computation happening behind the scenes, this makes the ViewModel reusable.

When using repository between ViewModel and Model, ViewModel only knows how to make data request and repository will take care of how the data will be fetched, whether it may be from APIs or the local DB.

Since we are using ViewModel and LiveData which are Activity lifecycle aware, it will lead to fewer crashes and memory leaks.

ViewModelsThis class is used to store and manage UI related data in a lifecycle conscious way.

It lets data to survive configuration changes such as screen rotation.

Activity lifecycle + ViewModelSo even after the activity is re-created the data survives inside the view model class.

LiveDataLiveData is an observable data holder class.

Unlike other observables, it is lifecycle aware i.

e.

it is aware of the lifecycle of the other app components such as activities, fragments or services.

This means it only updates app component observers when they are in an active lifecycle state.

DataBindingIt is a support library that allows us to bind our UI component from layouts to a data source in our app using declarative format rather than programmatically.

We define our entire layout XML inside the <layout></layout> tag.

We use a <variable> tag to define custom objects which then can be used inside the layout views.

Navigation ComponentIt simplifies the implementation of navigation between the destinations in our app.

By default, it includes Support Fragments and Activities as the destinations.

LET’S CODE NOWCreating a projectLet us create a new project with name trending-git and package id com.

ankit.

trendinggitCreating a new projectAs you can see there is a check “Include Navigation Controller”, this will include the Jetpack Navigation Component in our project.

Now we are going to update the app gradle fileapply plugin: 'com.

android.

application'apply plugin: 'kotlin-android'apply plugin: 'kotlin-android-extensions'apply plugin: 'androidx.

navigation.

safeargs'apply plugin: 'kotlin-kapt’android {.

dataBinding { enabled = true }.

}dependencies {implementation"org.

jetbrains.

kotlin:kotlin-stdlib-jdk7:$kotlinVersion"implementation "androidx.

appcompat:appcompat:$rootProject.

appCompatVersion"implementation "androidx.

constraintlayout:constraintlayout:$rootProject.

constraintLayoutVersion"// Navigation componentimplementation "android.

arch.

navigation:navigation-fragment:$rootProject.

navVersion"implementation "android.

arch.

navigation:navigation-ui:$rootProject.

navVersion"implementation "android.

arch.

navigation:navigation-runtime-ktx:$rootProject.

navVersion"implementation "android.

arch.

work:work-runtime-ktx:$rootProject.

workVersion"// Ankoimplementation "org.

jetbrains.

anko:anko:$rootProject.

ankoVersion"implementation "org.

jetbrains.

anko:anko-commons:$rootProject.

ankoVersion"// Retrofitimplementation "com.

squareup.

retrofit2:retrofit:$rootProject.

retrofitVersion"implementation "com.

squareup.

retrofit2:converter-gson:$rootProject.

retrofitVersion"implementation "com.

squareup.

okhttp3:logging-interceptor:$rootProject.

okhttpVersion"// Picassoimplementation "com.

squareup.

picasso:picasso:$rootProject.

picassoVersion"// Databinding compilerkapt "com.

android.

databinding:compiler:$rootProject.

dataBindingCompilerVersion"}Update the project gradle filebuildscript {ext{kotlinVersion = '1.

2.

60'navVersion = '1.

0.

0-alpha04'workVersion = '1.

0.

0-alpha04'ankoVersion = '0.

10.

5'appCompatVersion = '1.

0.

0'constraintLayoutVersion = '1.

1.

3'retrofitVersion = '2.

3.

0'okhttpVersion = '3.

9.

1'picassoVersion = '2.

71828'dataBindingCompilerVersion = '3.

2.

0-alpha10'gradleVersion = '3.

3.

0-alpha05'}repositories {google()jcenter()}dependencies {classpath "com.

android.

tools.

build:gradle:$gradleVersion"classpath "org.

jetbrains.

kotlin:kotlin-gradle-plugin:$kotlinVersion"classpath "android.

arch.

navigation:navigation-safe-args-gradle-plugin:$navVersion"// NOTE: Do not place your application dependencies here; they belong// in the individual module build.

gradle files }}allprojects { repositories { google() jcenter() }}Let us create the layout file activity_main.

xmlWe have created a toolbar and a fragment container.

This container will hold the fragments which will be displayed in the app.

The name of the container is androidx.

navigation.

fragment.

NavHostFragment because NavHost is an empty view upon which destinations are swapped in and out as the user will navigate through the App.

The navGraph attributes define the Navigation Graph which will hold these destinations (The destination can be an Activity or Fragment).

The attribute defaultNavHost ensures that your NavHostFragment intercepts the system Back button.

Now let’s create our MainActivity.

kt class fileIn MainActivity after setting the toolbar, NavigationUI.

setupActionBarWithNavController will bind our NavController with the action bar so that NavController can update the toolbar as the user navigates between the destinations.

These updates can be like setting up the title according to the destination and the menu items.

The action bar will also update the up button when the user is on a non-root destination.

At last, we have overridden the onSupportNavigateUp() method, this will delegate the system up button call with the NavController.

Now let’s see the magic of Navigation graph.

Since our app is having two fragment classes, RepoListFragment and RepoDetailFragment which we will define later on, the navigation graph looks like below.

res/navigation/nav_graph.

xmlThis navigation graph abstracts the fragment transactions happening under the hood.

This is the xml representation of navigation graph, now let’s see the visual representation.

nav_graph.

xmlThis reminds me of the Story Board in iOS, the iOS developers may relate to this.

So basically this is the UI representation of our navigation graph and it defines how the navigation is happening between the destinations.

This navigation graph is divided into three sections:Destination list — It is the left most section, it has two sub lists.

First is the Host, where we define our navigation host, in our case, it is “main_nav_fragmnet” which we have defined in our “activity_main.

xml” layout file.

Second is the Graph, which contains the list of our destinations that are “repoListFragment” and “repoDetailFragment” in our case.

Graph Editor — The middle section which contains the visual representation of our navigation graph.

Attribute Editor — The right most section, it contains the attributes related to destinations and actions.

An action can be seen as the line with an arrow between the destinations, it defines how the navigation will happen.

You can define transition animation, launch options, etc inside the action tag.

Now we will create our fragment, ViewModel and repository classes.

RepoListFragment.

ktInitialising data binding for our fragment class together with the viewmodel object which lies inside the layout as a variable of data binding object.

We have declared reference variables as lateinit, so that we can initialise them later on.

Inside setupObservers() we are observing two live datas that are repoListLive and toastMessage.

repoListLive is to update the latest repoList data from data source and toastMessage is just for displaying a toast message.

Finally setting up the adapter inside the setupAdapter() method.

fragment_repo_list.

xmlWe have used the layout tag in our layout to enable data binding in the fragment.

We have used livedata dataLoading and empty to hide and show the views in our layout.

We will now see how these livedata are being updated in our view model at runtime.

BaseViewModel.

ktWe have first created this base class BaseViewModel.

kt which is extending the ViewModel.

We can define the common live data inside this base class which can be used in other view model classes in the entire app.

RepoListViewModel.

ktRepoListViewModel is our view model class for RepoListFragment which is extending our BaseViewModel.

The live data repoListLive will hold the list of the post which will be listed in our fragment, inside our fragment we are already observing this live data.

So when we call the method fetchRepoList(), it will internally call the getRepoList from the repository we have created here and in the callback we will get the result which we will then set inside our live data, then this live data will update the observers which are present in our View (fragment in this case).

RepoRepository.

ktIn RepoRepository we are making an API call through Retrofit, on getting the result we are sending the data back through a callback function onResult (this is a lambda expression which is defined at the calling site).

A repository may have different data sources but here we have only one data source i.

e.

through a retrofit API call.

For detail code of the ApiClient please refer to the git repository here.

RepoListAdapter.

ktInside onCreateViewHoler we are again binding the data for the layout view_repo_list_item.

xml and passing the dataBinding and the repoListViewModel object to the constructor of RepoListViewHolder class.

Then inside onBindViewHolder method, we are setting the data for that list item.

The method updateRepoList is used to update the list inside our adapter, in our case it is called inside the observer of repoListLive.

Refer the git repository for the view_repo_list_item.

xml code.

RepoListViewHolder.

ktInside setup method firstly we are setting the itemData variable which is defined in the databinding scope of the view_repo_list_item.

xml layout, then calling executePendingBindings over dataBinding object will evaluate the pending bindings, updates any view that is expression bound to modified variables (it must be called over the UI thread).

Inside item onClick, we have created a bundle with key “url” and set the value from itemData and navigate to the corresponding destination which is defined inside the <action> tag having id “action_repoListFragment_to_repoDetailFragment ” in our navigation graph nav_graph.

xmlRepoDetailFragment.

ktFetching the url inside the onViewCreated method by RepoDetailFragmentArgs.

fromBundle(), this is an auto-generated class for the arguments that need to be passed to RepoDetailFragment since we have defined it inside the <argument> tag of our navigation graph and after getting the url we load it on the webview.

So, this was the basic implementation of MVVM architecture using Kotlin and Jetpack components.

The MVVM pattern is very powerful and still growing.

Please do share if any improvement or updation is required.

The complete code for the above project is available here.

.

. More details

Leave a Reply