Livedata 概览

LiveData 是一种可察看的数据存储器类。与惯例的可察看类不同,LiveData 具备生命周期感知能力

如果观察者(由 Observer 类示意)的生命周期处于 STARTED 或 RESUMED 状态,则 LiveData 会认为该观察者处于沉闷状态。。LiveData 只会将更新告诉给沉闷的观察者。为察看 LiveData 对象而注册的非沉闷观察者不会收到更改告诉。

您能够注册与实现 LifecycleOwner 接口的对象配对的观察者。有了这种关系,当相应的 Lifecycle 对象的状态变为 DESTROYED 时,便可移除此观察者。这对于 Activity 和 Fragment 特地有用,因为它们能够释怀地察看 LiveData 对象,而不用放心泄露

LiveData 劣势

  1. 数据合乎页面状态
  2. 不会产生内存泄露
  3. 不会因 activity 进行而导致解体
  4. 不再须要手动解决生命周期
  5. 数据始终保持最新状态
  6. 能够用来做资源共享

Livedata 应用

一般来说咱们会在 ViewModel 中创立 Livedata 对象,而后再 Activity/Fragment 的 onCreate 中注册 Livedata 监听(因为在 onStart 和 onResume 中进行监听可能会有冗余调用)

Livedata 简略应用

依然还是用咱们倒计时的例子,在 Viewmodel 中开始一个 2000s 的倒计时,而后通过 Livedata 回调给 Activity 进行更新界面,代码:

  1. viewmodel 代码
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("残余:${remainSecond} 秒")            }            override fun onFinish() {                countDownLivedata.postValue("倒计时完结")            }        }        countDown.start()    }}复制代码
  1. activity 中察看数据更新 ui 代码
 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                }            }        })    }复制代码
  1. 效果图

应用全局 Livedata 在多个视图监听状态

本例实现的 demo 成果是,创立一个全局的倒计时,而后在 Activity 中增加两个按钮,点击后能够切换 FragmentA 和 FragmentB。而后咱们通过全局的自定义 LiveData 单例实现数据监听,切换 Fragment 后 Fragment 页面上会展现倒计时的残余秒数

代码:

  1. 全局自定义 Livedata 代码
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        }    }}复制代码
  1. 倒计时器代码较长只粘贴一部分,有趣味能够到 github 去查看残缺代码
 private val listeners = mutableListOf<OnDataChangeListener>()    init {        val countDown = object : CountDownTimer(2000 * 1000, 1000) {            override fun onTick(millisUntilFinished: Long) {                remainSecond--                callback("残余:${remainSecond} 秒")            }            override fun onFinish() {                callback("倒计时完结")            }        }        countDown.start()    }    /**     * 循环遍历回调音讯     */    private fun callback(msg:String) {        for (listener in listeners){            listener.change(msg)        }    }复制代码
  1. FragmentA、FragmentB 中监听倒计时状态
GlobalLivedata.getInstance().observe(viewLifecycleOwner,            { t ->                inflate.findViewById<TextView>(R.id.tv_fragmentA).text = "fragmenta:${t}"            })复制代码
 GlobalLivedata.getInstance().observe(viewLifecycleOwner,            { t ->                inflate.findViewById<TextView>(R.id.tv_fragmentB).text = "fragmentb:${t}"            })复制代码
  1. 最终成果

最终成果,当咱们切换 Fragment 的时候两个 Fragment 显示的秒数是统一的,其实即便咱们马上启动一个新 activity 去查看残余秒数也是一样的,有趣味的敌人能够下载 git 代码本人尝试

对 Livedata 进行转换

map 和 switchMap 两个办法能够对已有的 Livedata 进行转换失去新的 Livedata

Transformation.map

在 activity 中察看 viewmodel 中的数据更新,当点击 activity 中按钮的时候会调用 viewmodel.sendData 办法发送数据,而后发送的数据会做肯定的转换给 activity,而后 activity 打印日志展现

间接看代码吧:

  1. 创立 viewmodel,model 中创立 Livedata
class TransMapViewModel: ViewModel() {    fun sendData() {        userLivedata.value=User("李白",1200)//对userLivedata进行复制    }    val userLivedata =MutableLiveData<User>()    val mapLiveData = Transformations.map(userLivedata){        "${it.name} : ${it.age}"//这里能够返回任意类型的数据    }}data class User(var name:String,var age:Int)复制代码

代码中 mapLiveData 是对 userLivedata 进行转换失去的,所以当咱们调用 sendData 办法更新 userLivedata 中的办法时,mapLiveData 的回调也会触发

  1. 在 activity 中察看 mapLiveData 并点击按钮发送小数据
 mapViewModel.mapLiveData.observe(this,{            logEE(it)            tv_map.text=it        })        btn_map.setOnClickListener {            mapViewModel.sendData()        }复制代码

Transformation.switchMap

本例中咱们实现如下逻辑:

在 activity 中察看 viewmodel 中的数据更新,当点击 activity 中按钮的时候会调用 viewmodel.sendData 办法发送数据,而后发送的数据会做肯定的转换给 activity,而后 activity 打印日志展现

  1. viewmodel 中代码
class SwitchMapViewModel : ViewModel() {    fun sendData() {        userLivedata.value = SwitchUser("李白", 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)复制代码
  1. 调用局部代码
model.mapLiveData.observe(this, {            logEE(it)        })        btn_switchmap.setOnClickListener {            model.sendData()        }复制代码

合并两个 Livedata(MediatorLiveData)

设想这样一个场景,您的 app 外面有一个评论列表的性能,能够对列表内容进行点赞。每一个点赞都是一个异步任误,你的产品需要并不想让用户点太多赞,比方一分钟点赞数量不能超过 10 次,这种场景就很适宜用 Livedata 的合并性能

咱们就不模仿这么简单的场景了,咱们的例子做这样一个事件:

界面上有两个按钮,点一次相当于点赞一次,咱们点击十次按钮就在界面上展现文字提醒用户曾经点击了十次数据。

代码展现:

1.model 代码

class MeditorLiveViewModel : ViewModel() {    var count =0//计数字段    fun setData1(name: 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}次,再点我也不跟你玩了,收手吧。。。"        }    }}复制代码

model 中创立了三个 Livedata,其中两个别离是 livedata1 和 livedata2,别离对应其中两个按钮。

还有一个 liveCombind 用来回调超过十次调用的场景

init 办法中 liveCombind.addSource 调用就是示意用来两头拦挡 livedata1 和 livedata2 的数据更新,解决 count 累加和是否回调 liveCombind 的性能

  1. activity 中代码
  model.liveCombind.observe(this){            logEE(it)            tv_count.text=it        }        btn_livedata1.setOnClickListener {            model.setData1("李白")        }        btn_livedata2.setOnClickListener {            model.setData2(1000)        }复制代码
  1. 实现成果

observeForever

observeForever 办法也是注册 Livedata 监听的办法,示意即便应页面被笼罩处于不沉闷状态也能够收到数据扭转的回调

Livedata 和协程联结应用

emit 形式应用

  1. 引入依赖 有时候你可能须要解决异步工作,工作解决实现后刷新 ui

这种状况能够应用 Livedata 的扩大程序实现

本例咱们实现上面的逻辑:

在 viewmodel 中阻塞 4s,而后告诉 activity

代码:

  1. 引入依赖插件
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'复制代码
  1. 开启异步工作办法
 /**     * 开启异步工作     */    fun startAsyncWithSecond(second: Int): LiveData<String> = liveData<String> {        delay(second * 1000L)        emit("倒计时完结")//用来触发数据回调    }复制代码

当咱们调用 startAsyncWithSecond 办法的时候会马上返回一个 Livedata 对象,供咱们注册监听

  1. activity 中注册 livedata 监听
  model.startAsyncWithSecond(3).observe(this){                logEE(it)//model中delay  3s后会返回数据到这里            }复制代码
  1. 成果展现

emitSource 应用

应用 emitSource 的成果等同于 MediatorLiveData 的成果

咱们本例实现如下的成果:

点击按钮开启一个 3s 的异步工作,而后告诉 activity 打印日志。

而后再次开启一个 3s 的异步工作,完结后再次告诉 activity 打印日志

代码:

  1. 创立异步工作办法
 fun startAsyncEmitSource(second: Int)= liveData<String> {        delay(second * 1000L)        emit("${second} 秒阻塞实现,再阻塞三秒后告诉你")        val emitSourceLivedata = MutableLiveData<String>()        emitSource(            emitSourceLivedata        )        delay(second*1000L)        emitSourceLivedata.value="再次阻塞 ${second}秒实现"    }复制代码
  1. activity 中注册监听
  model.startAsyncEmitSource(3).observe(this){                logEE(it)            }复制代码
  1. 成果