Use of LiveData in JetPack

Keywords: Android jetpack

1. Background

Like its name, LiveData is an observable data holder. Unlike conventional observable, LiveData has life cycle awareness, which means that it can correctly handle the life cycle in activities, fragments and services.

The data source of LiveData is generally ViewModel, or other components that can update LiveData. When the data is updated, LiveData will notify all its observers, such as Activiy. Different from RxJava's method, LiveData does not notify all observers. It will only notify observers in Active state. If an observer is in paid or Destroyed state, it will not receive notification. This is particularly useful for Activiy and services because they can safely observe LiveData objects without worrying about memory leaks. Developers also do not need to unsubscribe from LiveData in the onPause or onDestroy methods. Another thing to note is that once the observer resumes the Resumed state, it will receive the latest data of LiveData again.

2. Basic usage of livedata

2.1 MutableLiveData usage

LiveData is an abstract class, and its simplest implementation class 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 code

The observe method at note 1 has two parameters: lifecycle owner and observer < T >, the first parameter is MainActivity itself, and the second parameter creates a new observer < string >, which is called back in the onChanged method. The postValue method at the comment will update the data in the main thread, which will get the printed result. In most cases, the observe method of LiveData will be placed in the onCreate method. If it is placed in the onResume method, it will be called multiple times. In addition to the postValue method of MutableLiveData, you can also use the setValue method. The difference between them is that the setValue method must be used in the main thread. If you update LiveData in the working thread, you can use the postValue method.

2.2 data update

If we want to change the values stored in the LiveData object before it is distributed to the observer, we can use Transformations.map() and Transformations.switchMap(), which are explained by simple examples below.

2.2.1 Transformations.map()

If you want to make changes to the values stored in the LiveData object before it is distributed to the observer, you can use Transformations.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 code

Through Transformations.map(), the string "+ Android update" is added to mutableLiveData.

2.2.2 Transformations.switchMap()

If you want to manually control and monitor the data changes of one of them and switch monitoring at any time as needed, you can use Transformations.switchMap(), which is similar to Transformations.map(), but 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 code

In note 1, create a mutablelivedata < Boolean > () to control switching and assign it to liveDataSwitch. mutableLiveData1 is returned when the value of liveDataSwitch is true, otherwise mutableLiveData2 is returned. In Note 2, update the value of liveDataSwitch to faske, so that the output result is "Android update 2", which achieves the purpose of switching monitoring.

2.3 merging multiple LiveData data data sources

MediatorLiveData inherits from mutableLiveData. It can collect multiple LiveData data data sources, so that one component can monitor the changes of multiple LiveData data 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 code

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

The printed result is: D/MainActivity: onChanged1:Android update

3. Expand LiveData objects

If the observer's lifecycle is in the STARTED or RESUMED state, LiveData treats the observer as Active. The example of how to extend LiveData on the official website is relatively concise, 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 code

The above code is an example of observing stock changes. It expands LiveData and implements the two empty methods onActive and onInactive of LiveData. The onActive method will be called when the number of observers in Active state changes from 0 to 1. Generally speaking, the onActive method will be called when the LiveData object has observers in Active state. The update of stock price should be observed in the onActive method. When the LiveData object does not have any Active observers, the onInactive method is called, in which the connection with the StockManager service is disconnected.

Use StockLiveData in Fragment, as shown below.

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

More advanced Android knowledge can be obtained by scanning the code into the group~

 

 

Posted by gary00ie on Fri, 03 Sep 2021 22:02:48 -0700