1. The background

LiveData is, as its name suggests, an observable data holder. Unlike a normal Observable, LiveData is lifecycle aware, which means it can properly handle life cycles in activities, fragments, and services.

The data source for LiveData is typically a ViewModel, or it can be any other component that updates LiveData. When data is updated, LiveData notifies all its observers, such as Activiy. Unlike RxJava’s approach, LiveData does not notify all observers, but only those in the Active state. If an observer is Paused or Destroyed, it will not be notified. This is especially useful for Activiy and Service because they can safely observe LiveData objects without worrying about memory leaks. Developers also do not need to unsubscribe from LiveData in onPause or onDestroy. Another thing to note is that once the observer resumes its Resumed state, it will receive the latest data from LiveData again.

2. Basic usage of LiveData

2.1 MutableLiveData use

LiveData is an abstract class whose simplest implementation is MutableLiveData.

class MainActivity : AppCompatActivity() { protected fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val mutableLiveData: MutableLiveData<String> = MutableLiveData() mutableLiveData.observe(this, object : Observer<String? > { //1 fun onChanged(@Nullable s: String) { Log.d(TAG, "onChanged:$s") } }) mutableLiveData.postValue("java transfer kotlin") //2 } companion object { private const val TAG = "MainActivity" } }Copy the code

The observe method in comment 1 has two arguments LifecycleOwner and Observer

. The first argument is MainActivity itself, and the second argument creates a new Observer

, which is called back in the onChanged method. The postValue method at the comment updates the data in the main thread, so you get the printed result. In most cases, LiveData’s observe method will be placed in the onCreate method, and if it is placed in the onResume method, it will be called more than once. In addition to MutableLiveData’s postValue method, you can also use the setValue method. The difference is that the setValue method must be used on the main thread, whereas the postValue method can be used when updating LiveData in a worker thread.

2.2 Update of data

If we want before distributed to observer LiveData object to store the value of the changes, you can use Transformations. The map () and Transformations. SwitchMap (), with simple examples to explain them.

2.2.1 Transformations. The map ()

If you want to make changes to the values stored in LiveData objects before they are distributed to observers, you can use Mineral.map ().

class MainActivity : AppCompatActivity() { protected fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val mutableLiveData: MutableLiveData<String> = MutableLiveData() mutableLiveData.observe(this, object : Observer<String? > { fun onChanged(@Nullable s: String) { Log.d(TAG, "onChanged1:$s") } }) val transformedLiveData: LiveData = Transformations.map(mutableLiveData, object : Function<String? , Any? >() { fun apply(name: String): Any { return "$name+Android update" } }) transformedLiveData.observe(this, object : Observer { fun onChanged(@Nullable o: Any) { Log.d(TAG, "onChanged2:$o") } }) mutableLiveData.postValue("Android postValue") } companion object { private const val TAG = "MainActivity" } }Copy the code

You can add the string +Android update to mutableLiveData mineral.map ().

2.2.2 Transformations.switchMap()

If you want one manual control to monitor data changes, and can switch to monitor at any time according to need, then you can use Transformations. SwitchMap (), and Transformations. It use way similar to map (), Only switchMap() must return a LiveData object.

class MainActivity : AppCompatActivity() { var mutableLiveData1: MutableLiveData<String>? = null var mutableLiveData2: MutableLiveData<String>? = null var liveDataSwitch: MutableLiveData<Boolean>? = null protected fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) mutableLiveData1 = MutableLiveData() mutableLiveData2 = MutableLiveData() liveDataSwitch = MutableLiveData<Boolean>() //1 val transformedLiveData: LiveData = Transformations.switchMap(liveDataSwitch, object : Function<Boolean? , LiveData<String? >? >() { fun apply(input: Boolean): LiveData<String>? { return if (input) { mutableLiveData1 } else { mutableLiveData2 } } }) transformedLiveData.observe(this, object : Observer<String? > { fun onChanged(@Nullable s: String) { Log.d(TAG, "onChanged:$s") } }) liveDataSwitch.postValue(false) //2 mutableLiveData1.postValue("Android update 1") mutableLiveData2.postValue("Android update 2") } companion object { private const val TAG = "MainActivity" } }Copy the code

Create a new MutableLiveData

() at comment 1 to control the switch and assign it to liveDataSwitch. Return mutableLiveData1 when liveDataSwitch is true. Otherwise, return mutableLiveData2. Note 2 updates the value of liveDataSwitch to faske, so that the output is “Android update 2”, which has the purpose of switching listening.

2.3 Merge multiple LiveData sources

MediatorLiveData inherits from mutableLiveData, which can aggregate multiple LiveData sources and enable a component to monitor changes of multiple LiveData data.

class MainActivity : AppCompatActivity() {
    protected fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val mutableLiveData1: MutableLiveData<String> = MutableLiveData()
        val mutableLiveData2: MutableLiveData<String> = MutableLiveData()
        val liveDataMerger = MediatorLiveData<String>()
        liveDataMerger.addSource(mutableLiveData1, object : Observer {
            fun onChanged(@Nullable o: Any) {
                Log.d(TAG, "onChanged1:$o")
            }
        })
        liveDataMerger.addSource(mutableLiveData2, object : Observer {
            fun onChanged(@Nullable o: Any) {
                Log.d(TAG, "onChanged2:$o")
            }
        })
        liveDataMerger.observe(this, object : Observer {
            fun onChanged(@Nullable o: Any) {
                Log.d(TAG, "onChanged:$o")
            }
        })
        mutableLiveData1.postValue("Android update")
    }

    companion object {
        private const val TAG = "MainActivity"
    }
}
Copy the code

For a more intuitive example, LiveData and MediatorLiveData are placed in the same Activity. MediatorLiveData merges the two MutableLiveData together using the addSource of MediatorLiveData so that MediatorLiveData can sense when any MutableLiveData data changes.

The output is D/MainActivity: onChanged1:Android Update

3. Extend the LiveData object

LiveData treats the observer as Active if the observer’s life cycle is STARTED or RESUMED. The example on the official website on how to extend LiveData is very succinct, as shown below.

class StockLiveData private constructor(symbol: String) : LiveData<BigDecimal? >() { private val stockManager: StockManager private val listener: SimplePriceListener = object : SimplePriceListener() { fun onPriceChanged(price: BigDecimal?) { setValue(price) } } protected fun onActive() { stockManager.requestPriceUpdates(listener) } protected fun onInactive()  { stockManager.removeUpdates(listener) } companion object { private var sInstance: StockLiveData? = null @MainThread operator fun get(symbol: String): StockLiveData? { if (sInstance == null) { sInstance = StockLiveData(symbol) } return sInstance } } init { stockManager = StockManager(symbol) } }Copy the code

The code above is an example of observing stock movements and extends LiveData to implement its two empty methods onActive and onInactive. The onActive method is called when the number of observers in the Active state changes from 0 to 1. Generally speaking, the onActive method is called when the LiveData object has an Active observer, and the onActive method should start observing the update of the stock price. The onInactive method is called when the LiveData object does not have any observers in the Active state. In this method, the connection to the StockManager service is disconnected.

Use StockLiveData in the Fragment, as shown below.

class MyFragment : Fragment() {
    fun onActivityCreated(savedInstanceState: Bundle?) {
        StockLiveData.get(symbol).observe(this) { price -> }
    }
}
Copy the code