The android View Model is a class that has right for managing and preparing the data for a Fragment or an Activity. It also responsible the communication of the Activity / Fragment with the others application (e.g. calling the business logic classes)

What is the use of View Model in Android?

ViewModel is a component of Android Jetpack. The aim of ViewModel class is to manage UI-related data and to store in a lifecycle conscious way. The ViewModel class keep data safe when configuration changes such as screen rotations in portrait and landscape mode.

Benefits

While we rotate the android device from portrait to landscape mode and vice versa then the activity is restarted again and all the data is reloaded again. To prevent such kind of problem we can use ViewModel class to load data into view .

Problem

view model
Example How to implement ViewModel

Add dependancy to Build.gradle

def lifecycle_version = "2.3.1"
    implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"
MainActivity
class MainActivity : AppCompatActivity() {
    lateinit var mainViewModel: MainViewModel
    lateinit var  txtCounter:TextView
    lateinit var  btnCounter:Button

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        mainViewModel=ViewModelProvider(this).get(MainViewModel::class.java)

        txtCounter=findViewById(R.id.textView)
        txtCounter.text=mainViewModel.count.toString()

        btnCounter=findViewById(R.id.button)
        btnCounter.setOnClickListener {
            increamentCounter()
        }

    }

    private fun increamentCounter() {

        mainViewModel.increament()
        txtCounter.text=mainViewModel.count.toString()
    }

}
MainViewModel
class MainViewModel : ViewModel() {
    var count:Int=0
    fun increament()
    {
        count++
    }
}

Note

The ViewModel class also helps in implementing MVVM(Model-View-ViewModel) architecture pattern which is recommended by Google for Android app .

 there are some  other advantages of using ViewModel class provided by Android framework like:

  • Handle configuration changes: when activity is recreated due to configuration changes then ViewModel objects are automatically retained 
  • Lifecycle Awareness: ViewModel objects are lifecycle-aware. They are automatically cleared when the Lifecycle they are observing gets destroyed.
  • Data Sharing: Sharing of data  between fragments in an activity using ViewModels much easier 
  • Kotlin-Coroutines support: ViewModel includes support for Kotlin-Coroutines. So, they can be easy for using  any asynchronous processing.
  • View model classes are independent from view class. They are only use for hold data and don’t aware who to use data and how to represent data, make sure you should have only data not a reference or particular view if you do so have have to introduce memory leak.
  • We can not create view model object on our own. We use view model provider to create object of view model
  • But view model create object with no arg constructor.

Suppose we want to pass argument in view model constructer ,but we can’t not create view model object so how can we do this?

We can solve this problem using view model factory class, view model factory create objects of view model as per our requirement.

View Model Factory

View Model Factory is responsible to create your instance of ViewModel. In this you can create object of view model with a parameterised constructor. ViewModelProvider.Factory is an interface. It have create method which is responsible for creating our VeiwModel’s instance.

As you can not call ViewModel constructor in Activity or Fragment You pass your ViewModel arguments to the ViewModelProvider.Factory using constructor or any other pattern like (Singleton, FactoryPattern etc.).when you are initializing  ViewModel.

When to use ViewModelProvider.Factory?

If your ViewModel class have dependencies then you should pass this dependencies through the constructor. so you can use that dependencies and test your ViewModel.

When not to use ViewModelProvider.Factory

If your ViewModel class don’t have any dependencies then you will not need ViewModelProvider.Factory. 

How to create view model factory class

You have to create a class and extend it with ViewModelProvider.Factory and implement it’s methods

class MainViewFactory(val counter :Int) :ViewModelProvider.Factory {
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        return MainViewModel(counter) as T

    }
}

How to use With live Data

Example 1:-

Gradle file

apply plugin: 'com.android.application'

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.2"
    defaultConfig {
        applicationId "com.test.viewmodeldemo1"
        minSdkVersion 22
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
     dataBinding{
         enabled = true
     }
}

dependencies {
    def lifecycle_version = "2.2.0"
    def arch_version = "2.1.0"
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.core:core-ktx:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    // ViewModel
    implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"
    // LiveData
    implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version"
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

MainActivity

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    private lateinit var viewModel: MainActivityViewModel
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        viewModel = ViewModelProvider(this).get(MainActivityViewModel::class.java)

        viewModel.count.observe(this, Observer {
          binding.countText.text = it.toString()
        })

        binding.button.setOnClickListener {
           viewModel.updateCount()
        }
    }
}

MainActivityViewModel

class MainActivityViewModel : ViewModel() {
    var count = MutableLiveData<Int>()

    init {
        count.value = 0
    }

    fun updateCount(){
        count.value = (count.value)?.plus(1)
    }
}

XML

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">
<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/count_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="66sp"
        android:textStyle="bold"
        android:typeface="serif"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.262" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Click Here"
        android:textSize="30sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

Example 2

Gradle file

apply plugin: 'com.android.application'

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.2"
    defaultConfig {
        applicationId "com.test.viewmodeldemo"
        minSdkVersion 22
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    dataBinding{
        enabled = true
    }
}

dependencies {
    def lifecycle_version = "2.1.0"
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'androidx.appcompat:appcompat:1.0.2'
    implementation 'androidx.core:core-ktx:1.0.2'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    // ViewModel and LiveData
    implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
}

MainActivity

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    private lateinit var viewModel: MainActivityViewModel
    private lateinit var viewModelFactory: MainActivityViewModelFactory
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        viewModelFactory = MainActivityViewModelFactory(125)
        viewModel = ViewModelProvider(this,viewModelFactory).get(MainActivityViewModel::class.java)

        viewModel.totalData.observe(this, Observer {
            binding.resultTextView.text = it.toString()
        })

        binding.insertButton.setOnClickListener {
            viewModel.setTotal(binding.inputEditText.text.toString().toInt())
        }
    }
}

MainActivityViewModel

class MainActivityViewModel(startingTotal : Int) : ViewModel() {
     private var total = MutableLiveData<Int>()
     val totalData : LiveData<Int>
     get() = total

    init {
        total.value = startingTotal
    }

    fun setTotal(input:Int){
        total.value =(total.value)?.plus(input)
    }
}

MainActivityViewModelFactory

class MainActivityViewModelFactory(private val startingTotal : Int) : ViewModelProvider.Factory{

    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(MainActivityViewModel::class.java)){
            return MainActivityViewModel(startingTotal) as T
        }
        throw IllegalArgumentException("Unknown View Model Class")
    }
}

Activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <EditText
            android:id="@+id/input_edit_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:ems="10"
            android:inputType="number"
            android:textSize="36sp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.323"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.064" />

        <Button
            android:id="@+id/insert_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="50dp"
            android:layout_marginTop="40dp"
            android:text="Add"
            android:textSize="36sp"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/input_edit_text" />

        <TextView
            android:id="@+id/result_text_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="45dp"
            android:layout_marginTop="32dp"
            android:layout_marginEnd="41dp"
            android:textSize="36sp"
            app:layout_constraintEnd_toEndOf="@+id/insert_button"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/insert_button" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

Conclusion

ViewModelProvider.Factory is responsible for create your instance of ViewModel. If your ViewModel have dependencies and you want to test your ViewModel then you should create your own ViewModelProvider. Factory and passed dependency using ViewModel constructor and give value to the ViewModelProvider.Factory instance.

Also read

You may also like...

0 Comments

No Comment.