一、LiveData 是什么?
官方也给了介绍说明,传送门:LiveData
LiveData 通常配合 ViewModel 一起用,一半的功能依靠 Lifecycle 实现,是一种可观察的数据存储器。与常规的可观察类不同,LiveData 具有生命周期感知能力,遵循其他应用组件的生命周期,这种感知力确保其处于活跃生命周期装填的应用组件观察者,通俗的讲就是 LiveData 自己充当两个角色,既是观察者,也是被观察者,可以实现双向绑定,既可以存也可以修改,数据驱动UI,STARTED
和 RESUMED
状态才会工作。
所以要掌握 LiveData 一定要明白 Lifecycle 的内容,可以参考上一篇 Lifecycle 的分析。
二、使用
示例1
该示例演示最简单的使用,先定义一个懒加载的 LiveData 数据单例类
object MyLiveData {
val info1: MutableLiveData<String> by lazy {
MutableLiveData() }
}
在 Activity 里面使用非常简单:
/**
* 触发与改变
*/
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main_livedata)
// 实际项目中一般使用DataBinding
val textView: TextView = findViewById(R.id.tv_textview)
// 1.观察者 kotlin特性lambda简写
MyLiveData.info1.observe(this, {
textView.text = it// 数据驱动UI
})
// 完整写法
/*MyLiveData.info1.observe(this, object : Observer<String> {
override fun onChanged(t: String?) {
textView.text = t
}
})*/
// 2. 触发数据改变
// 主线程修改数据setValue
MyLiveData.info1.value = "default"
// 子线程异步操作postValue
thread {
Thread.sleep(3000)
MyLiveData.info1.postValue("3秒钟修改数据")
}
thread {
Thread.sleep(6000)
MyLiveData.info1.postValue("6秒钟修改数据")
}
}
}
示例2
这个示例演示只在界面可见的情况刷新UI数据。
同样的,先定义一个懒加载的 LiveData 数据单例类
object MyLiveData {
val data1: MutableLiveData<String> by lazy {
MutableLiveData() }
init {
data1.postValue("init data")
//data1.value = "default"
}
/*
如果是主线程中初始化,用setvalue就会崩溃:
2021-11-16 16:32:41.651 12633-12780/sun.geoffery.myjetpack E/AndroidRuntime: FATAL EXCEPTION: Thread-2
Process: sun.geoffery.myjetpack, PID: 12633
java.lang.ExceptionInInitializerError
at sun.geoffery.myjetpack.livedata.simple2.MyService$onStartCommand$1.invoke(MyService.kt:18)
at sun.geoffery.myjetpack.livedata.simple2.MyService$onStartCommand$1.invoke(MyService.kt:15)
at kotlin.concurrent.ThreadsKt$thread$thread$1.run(Thread.kt:30)
Caused by: java.lang.IllegalStateException: Cannot invoke setValue on a background thread
at androidx.lifecycle.LiveData.assertMainThread(LiveData.java:487)
at androidx.lifecycle.LiveData.setValue(LiveData.java:306)
at androidx.lifecycle.MutableLiveData.setValue(MutableLiveData.java:50)
at sun.geoffery.myjetpack.livedata.simple2.MyLiveData.<clinit>(MyLiveData.kt:10)
at sun.geoffery.myjetpack.livedata.simple2.MyService$onStartCommand$1.invoke(MyService.kt:18)
at sun.geoffery.myjetpack.livedata.simple2.MyService$onStartCommand$1.invoke(MyService.kt:15)
at kotlin.concurrent.ThreadsKt$thread$thread$1.run(Thread.kt:30)
LiveData:assertMainThread("setValue");
*/
}
设计一个后台服务,模拟服务端连续不断地推送过来消息:
/**
* Service服务是主线程
*/
class MyService : Service() {
override fun onBind(p0: Intent?): IBinder? = null
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
thread {
for (str in 1..100000) {
Log.d("server", "服务器push消息(模拟滴滴提示音),内容是:${
str}")
MyLiveData.data1.postValue("服务器推来了消息,内容是:${
str}")
Thread.sleep(2000)
}
}
return super.onStartCommand(intent, flags, startId)
}
}
最后在界面应用:
/**
* 只在界面可见的情况刷新UI数据
*/
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main_livedata2)
val button: Button = findViewById(R.id.button)
button.setOnClickListener {
// 启动服务
startService(Intent(this, MyService::class.java))
Toast.makeText(MainLivedata2Activity@ this, "", Toast.LENGTH_SHORT).show()
}
// 如果在这里初始化了,LiveData 的init必须异步线程操作数据
MyLiveData.data1.observe(this, {
Log.d("server", "界面可见说明用户再鼓捣手机,更新消息界面:${
it}")
Toast.makeText(MainLivedata2Activity@ this, "更新界面UI成功:${
it}", Toast.LENGTH_SHORT).show()
})
}
}
示例3
该示例展示 LiveData 的数据粘性问题,先来一个数据源:
object MyLiveData {
val value1: MutableLiveData<String> by lazy {
MutableLiveData() }
}
主界面很简单,就是一个页面跳转,跳转前做了一个数据更新: