LiveData 是一种可观察的数据存储器类,与常规的Obserable类不同,LiveData 可感知应用组件(如 Activity、Fragment 或 Service)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。
如果观察者(由 Observer 类表示)的生命周期处于 STARTED 或 RESUMED 状态,则 LiveData 会认为该观察者处于活跃状态。LiveData 只会将更新通知给活跃的观察者。为观察 LiveData 对象而注册的非活跃观察者不会收到更改通知。
可以与Lifecycle配合使用,当相应的 Lifecycle对象的状态变为 DESTROYED 时,便可移除此观察者,避免出现内存泄漏。
Lifecycle详解
ViewModel详解
一:使用LiveData的优势
- 保持UI与数据的一致性:LiveData 遵循观察者设计模式,生命周期发生变化时,LiveData 会通知对应的应用程序组件(Observer),数据发生变化时也会通知更新 UI.
- 避免内存泄漏:这个 Observer 绑定了 Lifecycle 对象,当 Lifecycle 对象生命周期 destory 之后,这些 Observer 也会被自动清理。
- 避免 Activity 处于不活跃状态的时候产生崩溃:如果观察者(Observer)处于不活跃状态,则 Observer 不会接收任何 LiveData 事件。
- 不在手动处理生命周期:UI 组件只是观察相关数据,而不会停止或恢复观察,LiveData 会根据具体生命周期的变化而自动管理。
- 始终保持最新数据:如果生命周期为非活跃状态,则会在由非活跃状态转为活跃状态时接收最新数据,如从后台切换到前台自动接收最新数据。
- 正确处理配置更改:如果 Activity 或 Fragment 因为设备配置发生变化而重新创建,比如屏幕旋转等,也将会立即重新接收最新数据。
- 共享服务:可以借助 LiveData 数据的观察能力,根据 Livecycle 的生命周期状态随时连接或断开服务。
二:添加依赖
在app的build.gradle里添加如下
def lifecycle_version = "2.2.0"
// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
// LiveData
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
// Lifecycles only (without ViewModel or LiveData)
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
三:使用LiveData
1 创建LiveData,一般我们创建LiveData是在ViewModel中完成。
class NameViewModel : ViewModel() {
val currentName: MutableLiveData<String> by lazy { MutableLiveData<String>() }
}
我们定义一个NameViewModel集成ViewModel,并在ViewModel中声明MutableLiveData类型的变量currentName。
- 注意:请确保用于更新界面的 LiveData 对象存储在 ViewModel 对象中,而不是将其存储在 Activity 或 Fragment 中,避免 Activity 和 Fragment 过于庞大,并且解耦。Activity 和 Fragment只负责显示数据,不负责存储数据状态。将 LiveData 实例与特定的 Activity 或 Fragment 实例分离开,并使 LiveData 对象在配置更改后继续存在。
2 在Activity/Fragment中创建Observer,并将其订阅上LiveData,当LiveData对象的数据更改时,会回调到Observer的onChanged方法。
class LiveDataTestActivity :AppCompatActivity(){
private lateinit var model: NameViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_livedatatest)
model = ViewModelProvider(this).get(NameViewModel::class.java)
// 创建Observer
val nameObserver = Observer<String> {
tv_name.text = it
}
// 使用liveData的observer方法,将lifecycleOwner跟Observer作为参数传入,执行订阅
model.currentName.observe(this,nameObserver)
// 修改liveData的值,界面会自动更新UI
tv_name.setOnClickListener{
model.currentName.value = "名字叫:Chen Click"
}
}
}
- val nameObserver = Observer { tv_name.text = it } 这是创建Observer,这里是lamda的写法,不使用lamda的写法是
这是创建Observer,当LiveData有数据更改的时候,就会回调到该onChanged方法中val nameObserver2 = object:Observer<String>{ override fun onChanged(t: String?) { } } - model.currentName.observe(this,nameObserver) 使用LiveData的observe方法,传入LifecycleOwner与observer去订阅LiveData。使用LifecycleOwner的好处是自己管理生命周期,会自动帮我们removeObserver。
注意:我们也可以使用 observeForever(Observer) 方法在没有关联的 LifecycleOwner 对象的情况下注册一个观察者。在这种情况下,观察者会被视为始终处于活跃状态,因此它始终会收到关于修改的通知。但我们必须手动去调用 removeObserver(Observer) 方法来移除这些观察者,否则会存在内存泄漏
class LiveDataTestActivity :AppCompatActivity(){
private lateinit var model: NameViewModel
private var nameObserver:Observer<String>?=null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_livedatatest)
model = ViewModelProvider(this).get(NameViewModel::class.java)
// 创建Observer
nameObserver = Observer<String> {
tv_name.text = it
}
// 使用observeForever需要自己在合适的地方removeObserver,否则会存在内存泄漏
model.currentName.observeForever(nameObserver!!)
// 修改liveData的值,界面会自动更新UI
tv_name.setOnClickListener{
model.currentName.value = "名字叫:Chen Click"
}
}
override fun onDestroy() {
model.currentName.removeObserver(nameObserver!!)
super.onDestroy()
}
}
使用LiveData的observeForever方法,需要在onDestroy的时候,model.currentName.removeObserver(nameObserver!!)。避免内存泄漏。
3 更新LiveData的值,UI界面自动更新
tv_name.setOnClickListener{
model.currentName.value = "名字叫:Chen Click"
}
- 我们可以使用MutableLiveData的setValue或者postValue方法去修改LiveData的值,修改完后界面会自动更新UI 注意:您必须调用 setValue(T) 方法以从主线程更新 LiveData 对象,而postValue(T)是在可以用在子线程中更新LiveData
四:自定义LiveData
继承LiveData,重写onActive跟onInactive方法,下面例子,监听网络变化
class NetWorkLiveData(val context: Context) :LiveData<String>(){
companion object{
const val NETWORK_ACTION = "android.net.conn.CONNECTIVITY_CHANGE"
}
private val intentFilter = IntentFilter().apply {
addAction(NETWORK_ACTION)
}
private val mReceiver =object:BroadcastReceiver(){
override fun onReceive(context: Context?, intent: Intent?) {
if(intent?.action == NETWORK_ACTION){
value = "网络状态改变"
}
}
}
override fun onActive() {
context.registerReceiver(mReceiver,intentFilter)
super.onActive()
}
override fun onInactive() {
context.unregisterReceiver(mReceiver)
super.onInactive()
}
}
- onActive() 当有一个处于活跃状态的观察者监听LiveData时会被调用,我们的例子是表示注册了网络监听
- onInactive():当没有任何处于活跃状态的观察者监听LiveData时会被调用。我们这里例子是由于没有监听了,所以就取消注册网络监听的广播。
- setValue():更新LiveData的值,并通知到观察者。上面例子调用是value = "网络状态改变"
在Activity中使用自定义的LiveData,例子如下:
class LiveDataTestActivity :AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_livedatatest)
NetWorkLiveData(this).observe(this, Observer {
tv_name.text = it
})
}
}
NetWorkLiveData(this).observe(this, Observer { tv_name.text = it }) 添加如上代码,这样改变网络状态,那么text的值就会改变。
五:LiveData转换
有时候LiveData存储的类型不是我们想要的类型,这时候可以考虑使用转换,转成我们需要的类型,LiveData转换主要有两种变换:map和switchMap,都是Transformations类提供的。
5.1:map转换
class TestViewModel :ViewModel(){
val ageLiveData = MutableLiveData<Int>()
val nameLiveData = Transformations.map(ageLiveData, Function<Int,String> {
"名字是$it"
})
}
- 上面例子是将Int转成String的LiveData,map是直接修改返回的值和类型
5.2:switchMap转换
class TestViewModel :ViewModel(){
val ageLiveData = MutableLiveData<Int>()
fun getGradeName(age:Int):LiveData<String>{
val liveData = MutableLiveData<String>()
liveData.value = "年级是$age"
return liveData
}
val gradeLiveData = Transformations.switchMap(ageLiveData,Function<Int,LiveData<String>>{
getGradeName(it)
})
}
- 上面例子是switchMap将Int转成String的,switchMap变换需要返回一个LiveData对象,这就是跟map变换的区别。
六:合并多个LiveData数据
如果有多个LiveData,可以使用MediatorLiveData合并。一旦其中一个发生改变,就会通知MediatorLiveData。举例如下:
class NameViewModel : ViewModel() {
val currentName: MutableLiveData<String> by lazy { MutableLiveData<String>() }
val courseName: MutableLiveData<String> by lazy { MutableLiveData<String>() }
val media:MediatorLiveData<String> by lazy { MediatorLiveData<String>() }
init {
media.addSource(currentName) { media.value = it }
media.addSource(courseName) { media.value = it }
}
}
- media 通过addSource添加上currentName跟courseName这两个liveData 在Activity中使用
class LiveDataTestActivity :AppCompatActivity(){
private lateinit var model: NameViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_livedatatest)
model = ViewModelProvider(this).get(NameViewModel::class.java)
// 创建Observer
val nameObserver = Observer<String> {
if(it.startsWith("name")){
tv_name.text = it
}else{
tv_course_name.text = it
}
}
model.media.observe(this,nameObserver)
// 修改liveData的值,界面会自动更新UI
tv_name.setOnClickListener{
model.currentName.value = "name 名字叫:Chen Click"
}
tv_course_name.setOnClickListener{
model.courseName.value = "course 课程叫:数学"
}
}
}
- model.media.observe(this,nameObserver) 添加观察。
- model.currentName.value = "name 名字叫:Chen Click"。只要currentName值一改变就会通知到media注册的nameObserver
- model.courseName.value = "course 课程叫:数学"。只要courseName值一改变也会通知到media注册的nameObserver
这就是MediatorLiveData的用法,通过addSource合并多个LiveData的数据。可以通过removeSource删除LiveData的数据。举个简单removeSource的例子
mediatorLiveData.addSource(liveData1, new Observer() {
private int count = 1;
@Override public void onChanged(@Nullable Integer s) {
count++;
mediatorLiveData.setValue(s);
if (count > 10) {
mediatorLiveData.removeSource(liveData1);
}
}
});
以上就是LiveData的所有支持点