Skip to content

This Sample aim to understand the concepts of two main Jetpack components - ViewModel and LiveData with Kotlin as Programming language. Please read the ReadMe file for the theoretical description.

RahulSinghRawatDev/ViewModel_LiveData_Kotlin_Sample

Repository files navigation

ViewModel_LiveData_Kotlin_Sample

This Sample aim to understand the concepts of two main Jetpack components - ViewModel and LiveData with Kotlin as Programming language. Please read the ReadMe file for the theoretical description.

ViewModel

1. ViewModel is a class responsible for preparing and managing data to UI components(Activity or fragments) and also survives in orientation changes.

2. It remains alive until its reference scope is alive.

3. It should never access your view hierarchy or hold any reference of UI.

4. It was announced in google IO 2017.

5. Another important use of ViewModel is that it pcan be used a communication layer between different fragments of activity so that they nether need to talk to other fragment directly.

6. OnCleared() method is last method called inside ViewModel before its destruction.Useful if you want to clear some references in order to prevent memory leak.

7. If you want context inside your Viewmodel then use AndroidViewModel inside it as it have default application context inside it.

* Disadvantage of ViewModel over SavedInstanceState is It do not survice on process kill due to low memory but SavedInstanceState does.

Implementation of ViewModel

Create a class and Extends a ViewModel class inside it.


class MainViewModel : ViewModel() {

}

Create a ViewModelProvider instance inside your UI referencing that class which extends ViewModel class.Earlier it was ViewModelProviders but it is deprecated now.


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

ViewModelProvider

An utility class that provides ViewModel for a scope.

ViewModelProvider.Factory is use to create a custom constructor inside ViewModel.


class MyViewModelFactory(val arg:Int) :ViewModelProvider.Factory
{
    override fun  create(modelClass: Class): T {
        return modelClass.getConstructor(Int::class.java).newInstance(arg)
    }
}

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

How ViewModel survives rotation changes

When you write ViewModelProvider(this).get(MainViewModel::class.java) then ViewModelProvider map the ViewModel instance with "this" referencing UI component. For the first time it creates an instance of ViewModel then when you rotate the device then it uses the same instance of ViewModel a Singleton pattern is internally used for saving the state of ViewModel.

LiveData

It is an observable Data holder class. Its Lifecycle aware.

Its instance is created inside the ViewModel class and Observer inside the UI(Activity or fraagment).

It only trigger on Active UI thus saving you from OutOfMemory and UI related crashes.

Ways to Transform liveData

1. Map() - apply function to output of A liveData and send Result to LiveData B.

2. SwitchMap() - apply function that swaps LiveData observer is listening to.

3. MediatorLiveData - It is subclass of liveData.LiveData without mmultiple sources for custom transformation.

* Both Map() and SwitchMap() returns MediatorLiveData object.



<**************************  Internal Implementation of Map and SwitchMap  *********************>
@MainThread
    public static  LiveData map(@NonNull LiveData source,

        @NonNull final Function func) {
        final MediatorLiveData result = new MediatorLiveData<>();
        result.addSource(source, new Observer() {
            @Override
            public void onChanged(@Nullable X x) {
                result.setValue(func.apply(x));
            }
        });
        return result;
    }

   @MainThread
    public static  LiveData switchMap(@NonNull LiveData trigger,

        @NonNull final Function> func) {
        final MediatorLiveData result = new MediatorLiveData<>();

        result.addSource(trigger, new Observer() {
            LiveData mSource;

            @Override
            public void onChanged(@Nullable X x) {
                LiveData newLiveData = func.apply(x);
                if (mSource == newLiveData) {
                    return;
                }
                if (mSource != null) {
                    result.removeSource(mSource);
                }
                mSource = newLiveData;
                if (mSource != null) {
                    result.addSource(mSource, new Observer() {
                        @Override
                        public void onChanged(@Nullable Y y) {
                            result.setValue(y);
                        }
                    });
                }
            }
        });
        return result;
    }