This is the 12th day of my participation in the More text Challenge. For details, see more text Challenge

Follow my public account “Ananzhuo” to learn knowledge for free

Git address

Github.com/ananananzhu…

A large part of the text description is directly translated from the official website

Livedata overview

LiveData is an observable data storage class. Unlike regular observable classes, LiveData is lifecycle aware

If the life cycle of an Observer (represented by the Observer class) is in the STARTED or RESUMED state, Then LiveData considers the Observer to be active. LiveData will only notify active observers of updates. Inactive observers registered to observe LiveData objects do not receive change notifications.

You can register observers that are paired with objects that implement the LifecycleOwner interface. With this relationship, the observer can be removed when the state of the corresponding Lifecycle object becomes DESTROYED. This is particularly useful for activities and fragments, because they can safely observe the LiveData object without worrying about leaks

LiveData advantage

  1. The data conforms to the page state
  2. No memory leaks will occur
  3. It does not crash when the activity stops
  4. Manual processing of the lifecycle is no longer required
  5. The data is always up to date
  6. It can be used for resource sharing

Livedata use

Normally we would create a Livedata object in the ViewModel, Then register Livedata listening in the onCreate of the Activity/Fragment (because there may be redundant calls for listening in onStart and onResume)

Livedata is easy to use

Start a 2000s countdown in the Viewmodel, and then update the interface to the Activity by calling back to Livedata.

  1. The viewmodel code
class CountDownModel : ViewModel() {val countDownLivedata = MutableLiveData<String>() private var remainSecond = 2000 init {val countDown = object : CountDownTimer(2000 * 1000, 1000) { override fun onTick(millisUntilFinished: Long) {remainSecond -- countDownLivedata. PostValue (" surplus: "${remainSecond} seconds)} override fun onFinish () {countDownLivedata. PostValue (" "the end of the countDown)}} countDown. The start ()}}Copy the code
  1. Update UI code by observing data in the activity
val countDownModel: CountDownModel by viewModels<CountDownModel> { ViewModelProvider.NewInstanceFactory() } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_count_down) countDownModel.countDownLivedata.observe(this, object : Observer<String> { override fun onChanged(value: String?) { value? .let { tv_countdown_remainsecond.text = it } } }) }Copy the code
  1. rendering

Listen for status in multiple views using global Livedata

The demo effect of this example is to create a global countdown and then add two buttons to the Activity to switch between FragmentA and FragmentB. Then we implement data monitoring through global custom LiveData singleton. After switching Fragment, the Fragment page will display the remaining seconds of the countdown

Code:

  1. Global custom Livedata code
class GlobalLivedata : LiveData<String>() {
    val coundManager = CountDownManager()
    val listener = object : OnDataChangeListener {
        override fun change(data: String) {
           postValue(data)
        }
    }
    override fun onActive() {
        super.onActive()
        coundManager.setListener(listener)
    }

    override fun onInactive() {
        super.onInactive()
        coundManager.removeListener(listener)
    }
    companion object {
        private lateinit var globalData: GlobalLivedata
        fun getInstance(): GlobalLivedata {
            globalData = if (::globalData.isInitialized) globalData else GlobalLivedata()
            return globalData
        }
    }
}
Copy the code
  1. If the code is longer, just paste a part of it. If you are interested, go to Github to see the full code
private val listeners = mutableListOf<OnDataChangeListener>() init { val countDown = object : CountDownTimer(2000 * 1000, 1000) { override fun onTick(millisUntilFinished: Long) {remainSecond-- callback(" remainSecond: callback "); ${remainSecond} second ")} Override fun onFinish() {callback(" countDown to the end ")}} Countdown.start ()} /** * Loop through callback messages */ private fun callback(msg:String) { for (listener in listeners){ listener.change(msg) } }Copy the code
  1. Monitor the countdown state in FragmentA and FragmentB
GlobalLivedata.getInstance().observe(viewLifecycleOwner, {t -> findViewById<TextView>(R.I.D.tv_fragmentA).text = "FragmentA: ${t}"}) inflate.Copy the code
GlobalLivedata.getInstance().observe(viewLifecycleOwner, {t -> findViewById<TextView>(R.I.D.tv_fragmentB).text = "FragmentB: ${t}"}) inflate.Copy the code
  1. The final result

The final effect is that when we switch to the Fragment, the two fragments display the same number of seconds. In fact, even if we start a new activity to check the number of seconds left, it is the same. Interested friends can download git code to try it themselves

Convert the Livedata

The map and switchMap methods convert existing Livedata to new Livedata

Transformation.map

When you click the button in the activity, you will call the viewModel. SendData method to send the data, and then the sent data will be converted to the activity. The activity then prints the log display

Just look at the code:

  1. Create viewModel, create Livedata in model
class TransMapViewModel: ViewModel() {fun sendData() {userLiveData. value=User(" libai1 ",1200)// Copy userLivedata} val userLivedata =MutableLiveData<User>() val mapLiveData = Transformations.map(userLivedata){ "${it.name} : ${it. Age}" ${it. Age}" ${it. Age}" ${it.Copy the code

The mapLiveData in the code is converted from userLivedata, so when we call the sendData method to update the method in userLivedata, the mapLiveData callback is also triggered

  1. Observe mapLiveData in the activity and click the button to send the small data
 mapViewModel.mapLiveData.observe(this,{
            logEE(it)
            tv_map.text=it
        })
        btn_map.setOnClickListener {
            mapViewModel.sendData()
        }
Copy the code

Transformation.switchMap

In this example we implement the following logic:

When you click the button in the activity, you will call the viewModel. SendData method to send the data, and then the sent data will be converted to the activity. The activity then prints the log display

  1. The code in the viewmodel
class SwitchMapViewModel : ViewModel() {fun sendData() {userLiveData. value = SwitchUser(" libai.com ", 1200) } private val userLivedata = MutableLiveData<SwitchUser>() val mapLiveData = Transformations.switchMap(userLivedata) { changeUser(it!!) } private fun changeUser(it: SwitchUser): LiveData<String> {return MutableLiveData("${it. Name} ")}} data class SwitchUser(var name: String, var age: Int)Copy the code
  1. Call part code
model.mapLiveData.observe(this, {
            logEE(it)
        })
        btn_switchmap.setOnClickListener {
            model.sendData()
        }
Copy the code

Merge Two Livedata (MediatorLiveData)

Imagine a situation where you have a comment list in your app, and you can like the list. Each “like” is an asynchronous error, and your product needs don’t want people to “like” too much, such as no more than 10 likes per minute, which is a good scenario to use Livedata’s merge capabilities

Instead of simulating such a complex scenario, our example does something like this:

There are two buttons on the interface, one click equals one like, and we click the button ten times to display text on the interface indicating that the user has clicked the data ten times.

Code presentation:

1. The model code

Class MeditorLiveViewModel: ViewModel() {var count =0 String) { liveData1.value = name } fun setData2(age: Int) { liveData2.value = age } private val liveData1 = MutableLiveData<String>() private val liveData2 = MutableLiveData<Int>() val liveCombind = MediatorLiveData<String>() init { liveCombind.addSource(liveData1) { increase()  } liveCombind.addSource(liveData2) { increase() } } private fun increase() { count++ if(count==10){ LiveCombind. Value =" ${count} "liveCombind. Value =" ${count}" liveCombind. }}}Copy the code

Model creates three Livedata, two of which are LiveDatA1 and LiveDatA2, corresponding to two of the buttons.

There is also a scenario where liveCombind calls back and forth more than ten times

The call to liveCombind. AddSource in init method is the function that represents the interception of data updates to LiveDatA1 and LiveDatA2 in the middle, the count is added, and whether to call back to liveCombind

  1. The activity of the code
Model. LiveCombind. Observe (this) {logEE (it) tv_count. Text = it} btn_livedata1. SetOnClickListener {model. SetData1 (" li bai ")} btn_livedata2.setOnClickListener { model.setData2(1000) }Copy the code
  1. Implementation effect

observeForever

The observeForever method is also a way to register for Livedata listening, indicating that you can receive a callback for data changes even if the page should be overwritten and inactive

Livedata and coroutine are used together

Emit mode

  1. Sometimes you need to work on asynchronous tasks and refresh the UI when the tasks are finished

This can be done using extensions to Livedata

In this example we implement the following logic:

Block for 4s in the ViewModel and notify the activity

Code:

  1. Introducing dependent plug-ins
Implementation 'androidx. Lifecycle: lifecycle - livedata - KTX: 2.2.0'Copy the code
  1. Example Enable an asynchronous task method
/** * Start asynchronous task */ fun startAsyncWithSecond(second: Int): LiveData<String> = LiveData<String> {delay(second * 1000L) emit(" countdown to the end ")// trigger data callback}Copy the code

When we call the startAsyncWithSecond method we immediately return a Livedata object that we can register to listen to

  1. Register the LiveData listener in the activity
Model. StartAsyncWithSecond (3). Observe (this) {logEE (it) / / delay in the model will return after 3 s data here}Copy the code
  1. Results show

EmitSource use

Using emitSource has the same effect as using MediatorLiveData

In this example, we achieve the following effect:

Click the button to start a 3s asynchronous task and then notify the activity to print the log.

Then start a 3s asynchronous task again and notify the activity to print the log again when it is finished

Code:

  1. Create asynchronous task method
fun startAsyncEmitSource(second: Int)= liveData<String> {delay(second * 1000L) emit("${second} = ${second} ") val emitSourceLivedata = MutableLiveData<String>() emitSource(emitSourceLivedata) Delay (second*1000L) emitSourceliveData.value =" Blocking again ${second} "}Copy the code
  1. Register listeners in an activity
  model.startAsyncEmitSource(3).observe(this){
                logEE(it)
            }
Copy the code
  1. The effect

Finished writing this eyes have spent, only picked a typo, ready to rest five days to write Livedata source parsing

see you

Follow my public account “Ananzhuo” to learn knowledge for free