Skip to main content

Live Data. Difference between livedata and mutable live data. Mediator Live Data. Live Data Encapsulation

What is Live Data?  
Live Data is one of the first architectural components. Live Data is a data holder class. Not only a simple data holder class, but having lot of features. We can say that it is a simple, lifecycle aware, observable, data holder class.



Before introducing live data , we need to manually handle the updates in the UI. We were using traditional approaches like interfaces, events buses to update the UI. These traditional approaches having problem like manual handling of lifecycle, memory leaks, and crashes. 

Live Data

It is wrapper which can be used with any data such as data and objects that implements collections like list. A Live Data usually declared and used inside a view model class. Live Data can be used to observe the change of a particular data and update the UI based on that. Due to life cycle aware property we don't need to bother about components life cycle. i.e. it will send the update only when the component (activity/fragment)  is in active state. If the component is not active now and in future when it is active, live data will send the updated data to the component


Example of live data. 

private val _result= MutableLiveData<String>()  //in view model class

//in Activity/Fragment

wrapperViewModel.result.observe(this, Observer { 

    })

Features of Live Data

  • Life Cycle Aware 
  • Observable
  • Data holder
Live data is life cycle aware means that it can work together with the lifecycle of other app components such as activities, fragments and services. Live Data update the components observer only in the active state. If the observer's lifecycle is not active like onPause or onStop , then id doesn't receive any updates, hence it will reduce the number of crashes

If we want to communicate between two components , its quite difficult because we need to consider its life cycles and life spans. Live Data is based on the observable pattern which makes the communication between the view and view model easy. Live Data notifies Observer objects when the underlying data changes. Therefore you don't need to update the ui every time when the data changes, Observable will do it for you 

Advantages of Live Data

  • It keeps  UI updated
  • It will destroy automatically when the life cycle owner is destroyed
  • Ensures crashes
  • Can be shared with multiple resources

Types of Live Data

  • LiveData
  • MutableLiveData
  • MediatorLive Data
Live Data- It is an abstract class that cannot be instantiated and used directly .We cannot set values to the live data. But it can be used to observe the data.LiveData can be used to keep Mutable Live Data as backing field

val _score = MutableLiveData<String>()
val data: LiveData<String>get() = _score

Mutable Live Data-Mutable Live data is mutable.ie we can change the value. It can be achieved through setValue and postValue. If you are using  the main thread to update the value of Live Data, then you can use setValue. The value will be updated  and notify the observer. If you are working in the background thread and need to update the live data, then you can use postValue. It wil update the value  immediately but notifies the observer, and it will be scheduled to the UI thread  via the event loop  handler

Mediator LiveData - One to Many, It is a subclass of live data . It can be used to merge multiple live data into single live data which can be observed
LiveData liveData= MutableLiveData<String>()
val mediatorLiveData=MediatorLiveData<String>()
mediatorLiveData.addSource(liveData, object : Observer<Int> {   
            override fun onChanged(s: Int?) {
            }
        })

Mistakes when using Live Data 
Storing large data across transformation:- Live data is a data holder class and is holding some values. Therefore storing large values in live data and across transformation will lead to memory overhead. So never use large data holder value. Do only for short operation.
Sharing same instance of live data . Consider that you are using same instance of live data for two activities and imagine that two activities are sharing same instance at a time like activity transition where both the activities where active at the same time, then it will lead to bad UI experience, like if you were in the middle of animation then there will be a flash or glitch. Like that never share the same instance of live data from a singleton repository
  • If you need lot of operations or streams then never use live data, you can go for RX

  • If your operation is not about ui or lifecycle, then you can use callback interface

  • If you have short operation chaining , then you can use coroutine
Encapsulation in Live Data
By default , when we declare a live data in view model class it will be public. But as per the documentation in android official site, data of the view model must be modified within the view model class itself. It should not be modified outside view model in an activity or fragment. By making Mutable live data as backing field we can ensure encapsulation
val _score = MutableLiveData<String>()
val data: LiveData<String>get() = _score

Transformation in Live Data

We can use mutable live data for communicating between components and view model. Consider if there will be a third component like repository which also exposing live data, then how will we make subscription from view model since it doesn't have any life cycle aware
If you need a lifecycle in your view model then you need a transformation. Consider you have activity component, view model and repository. And if we want to make a bridge between view  and repository then you can use transformation map. 

When you establish that transformation , lifecycle is automatically carried over for you , if you run a transformation of couple of live data, when you subscribe it the lifecycle is automatically propagated  to inner live data

Transformation- map()

It is a one to one static transformation. 

LiveData<Y> map (LiveData<X> source, Function<X, Y> func)


Transformation -switchMap()
LiveData<Y> switchMap (LiveData<X> trigger, 
                Function<X, LiveData<Y>> func)



Live Data Example 
We can create a simple android application which is incrementing and decrementing value using live data 
Add the dependency file in the buid.gradle file 
def lifecycle_version = '2.5.1'

// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"

// LiveData
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<TextView
android:id="@+id/textValue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0"
android:textStyle="bold"
android:textColor="@color/black"
android:textSize="30sp"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="200dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>

<Button
android:layout_marginTop="50dp"
app:layout_constraintTop_toBottomOf="@+id/textValue"
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Increment"
app:layout_constraintRight_toLeftOf="@+id/textValue"
/>

<Button
android:layout_marginTop="50dp"
app:layout_constraintTop_toBottomOf="@+id/textValue"
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Decrement"
app:layout_constraintLeft_toRightOf="@+id/textValue"
/>


</androidx.constraintlayout.widget.ConstraintLayout>


MainActivity

package com.learning.livedataexample

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import com.learning.livedataexample.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {
private lateinit var viewModel: ActivityViewModel
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this,R.layout.activity_main)
viewModel= ViewModelProvider(this)[ActivityViewModel::class.java]
binding.incrementButton.setOnClickListener {
viewModel.incrementValue(getTextToInt())
}
binding.decrementButton.setOnClickListener {
if (binding.textValue.text.isNotEmpty())
viewModel.decrementValue(getTextToInt())
}
viewModel.result.observe(this, Observer {
binding.textValue.text=it.toString()
})
}

private fun getTextToInt():Int{
return if (binding.textValue.text.isNotEmpty())
Integer.parseInt(binding.textValue.text.toString())
else 0
}
}

ActivityViewModel.kt

package com.learning.livedataexample

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel

class ActivityViewModel:ViewModel() {

private val _result=MutableLiveData<Int>()

val result: LiveData<Int> get() = _result

fun incrementValue(data:Int){

this._result.value=data+1
}

fun decrementValue(data:Int){
this._result.value=data-1
}



}

OUTPUT





Comments

Popular posts from this blog

Capture image without surface view as background service in android

Capture image without user knowledge in android/Capture image as background service in android/Take photo without preview in android/ Camera without preview in android  package com.example.picture; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import android.app.Service; import android.content.Context; import android.content.Intent; import android.hardware.Camera; import android.hardware.Camera.Parameters; import android.media.AudioManager; import android.os.IBinder; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; public class CameraService extends Service {       //Camera variables       //a surface holder       private SurfaceHolder sHolder ;        //a variable to control the camera       private Camera mCamera ;       //the camera parameters       private Parameters parameters ;       /** Called when the activity is first created. */     @Override     p

Open front camera in android program

How to access front camera in android/get number of cameras in android/Program to open front camera in android Using the code explained below, we can easily find out number of cameras in your android device. Camera.CameraInfo cameraInfo = new Camera.CameraInfo();         Log. d ( "No of cameras" ,Camera. getNumberOfCameras ()+ "" );         for ( int camNo = 0; camNo < Camera. getNumberOfCameras (); camNo++) {             CameraInfo camInfo = new CameraInfo();             Camera. getCameraInfo (camNo, camInfo);                         if (camInfo. facing ==(Camera.CameraInfo. CAMERA_FACING_FRONT )) {                 mCamera = Camera. open (camNo);             }         }         if ( mCamera == null ) {            // no front-facing camera, use the first back-facing camera instead.            // you may instead wish to inform the user of an error here...               mCamera = Camera. open ();         } A sample program that take photo using front camer

Custom Spinner in Android

Custom Spinner in Android/Spinner using custom adapter/spinner with icon and text  In this tutorial, i'll show you to customizing spinner in different methods.  To change the normal display of spinner view, add background image using the code " android:background="@drawable/myspinner_background" in the xml. You can also make it customizable by setting up an xml file in drawable folder Custom display with icon in the spinner   main.xml <? xml version = "1.0" encoding = "utf-8" ?> < LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android"     android:orientation = "vertical"     android:layout_width = "fill_parent"     android:layout_height = "fill_parent"     >     < Spinner     android:drawSelectorOnTop = "true"     android:id = "@+id/spinner1"     android:layout_width = "match_parent"     android:layout_height = "wrap_content&