一、前言
LiveData 可观察数据存储类属于谷歌在2018推出Android jetpack(外网)其中的软件架构组件中的一个。在谷歌开发者网站有详细介绍LiveData(外网)。本文以总结为目的,采用更加清晰和有条理的方式解读LiveData。
阅读前准备
便于理解,如果你已经知道什么是Lifecycle、观察者模式和ViewModel 更好,那么理解此篇文章便轻车熟路。
避免过多而杂的介绍,部分知识不详述,请阅读笔者链接文章或者Google。
全文就一个核心
LiveData是结合Lifecycle和观察者模式的数据存储类
二、LiveData
谷歌爸爸的介绍
LiveData是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。
简单理解
LiveData就是结合生命周期+观察者模式的数据存储类。方便我们根据生命周期来监听数据变化的。由于这个结构的特性,所以它拥有以下特点
1、确保界面符合数据状态;2、不会发生内存泄露;3、不会因 Activity 停止而导致崩溃;4、不再需要手动处理生命周期;5、数据始终保持最新状态;6、适当的配置更改;7、共享资源
导入
implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"
三、详细介绍
LiveData介绍就如下几个点
- LiveData基本使用
- LiveData自定义LiveData
- LiveDa数据转换
- LiveData合并多个数据
3.1、基本使用
这里说下为啥别的文章或者官网都是结合ViewModel来讲的例子,ViewModel就是存储和管理界面数据的一个类。我们LiveData就是一种数据。如果不和ViewModel一起讲,不影响我们理解LiveData的核心思想。但是并不推荐直接在Activity或者Fragment里直接写LiveData变量来存储数据。这会让他们过于臃肿,所以还是用推荐存储数据的方式ViewModel。
如果你还不了解ViewModel你可以简单理解为它就是存储数据的一个地方。
LiveData在使用上除了和ViewModel结合以外无非观察者模式三个步骤
- 创建LiveData,即创建被观察者
- 添加Observe,添加观察者
- 数据改变是修改UI,观察者监听
public class MyViewModel extends ViewModel {
MutableLiveData name;
public MutableLiveData getName() {
if (name == null) {
name = new MutableLiveData<>();
}
return name;
}
}
ViewModelProvider.Factory factory = ViewModelProvider.AndroidViewModelFactory.getInstance(this.getApplication());
ViewModelProvider viewModelProvider = new ViewModelProvider(this, factory);
viewModel = viewModelProvider.get(MyViewModel.class);
viewModel.getName().observe(this, new Observer() {
@Override
public void onChanged(String s) {
textView.setText(s);
}
});
viewModel.getName().setValue("yink");
1、MyViewModel就是我们创建LiveData的地方。
2、viewModel.getName()拿到LiveData对象,observer添加观察者,this是Lifecycle生命周期组件。
3、当数据变化setValue时,会自动调用观察者的onChanged方法,我们就可以更新UI或其它操作了。
- 其中在理解ViewModel上可不必太追究细节,理解为创建对象即可
- 至于observe的参数this参数,带入的是Lifecycle,不太理解生命周期请移步Lifecycle
- 为啥用MutableLiveData对象是因为MutableLiveData提供set,get方法。而LiveData只对外开放get方法,LiveData的set,get代码如下:
protected void setValue(T value) {
public T getValue() {
3.2、自定义LiveData
public class MyLiveData extends MutableLiveData {
@Override
protected void onActive() {
super.onActive();
Log.d("yink","onActive");
}
@Override
protected void onInactive() {
super.onInactive();
Log.d("yink","onInactive");
}
}
自定义LiveData的时候,跟我们其它继承用法一样。说下onActive和onInactive什么时候调用
- onActive 两个条件:添加了observe + 生命周期alive。
- onInactive 移除了observe,或者生命周期非alive。
例:点击home到后台:onInactive; 重新进入页面:onActive ; 移除observe:onInactive
3.3、转换LiveData
Livecycle给我提供了两种方法来转换LiveData
Transformations.map
:把LiveData的值改变一下,目的是转换值
Transformations.switchMap
:每次改变值时提供一个全新的liveData对象并监听这个新的liveData对象
下面我用相同的例子来讲解一下他们的区别,便于大家理解
我有一个MutableLiveData
MutableLiveData adressNumber;
MutableLiveData adressStringOne = (MutableLiveData) Transformations.map(adressNumber, new Function() {
@Override
public String apply(Integer input) {
return getAdressString(input);
}
});
MutableLiveData adressStringTwo = (MutableLiveData) Transformations.switchMap(adressNumber, new Function>() {
@Override
public LiveData apply(Integer input) {
MutableLiveData mutableLiveData = new MutableLiveData();
mutableLiveData.setValue(getAdressString(input));
return mutableLiveData;
}
});
private String getAdressString(Integer number) {
if (number == 0755) return "深圳";
return "其它";
}
例子很简单,就是输入区号0755,查询出来是深圳。
map用法:
1、adressStringOne这个LiveData的值就是一个String,通过输入的0755来转换成”深圳“
2、adressStringOne对象没有变,每次监听adressNumber来设置自己的值
switchMap用法:
1、adressStringTwo这个LiveData同样拥有一个String值,通过输入的0755来转换成”深圳“
2、adressStringTwo和map用法不同的是,他拿到”深圳”这个值后,不是直接设置,而是new一个新的LiveData
原理
原理也比较简单,贴出源码结合我上面的例子就很好理解了。
public interface Function {
O apply(I input);
}
@MainThread
public static LiveData map(
@NonNull LiveData source,
@NonNull final Function mapFunction) {
final MediatorLiveData result = new MediatorLiveData<>();
result.addSource(source, new Observer() {
@Override
public void onChanged(@Nullable X x) {
result.setValue(mapFunction.apply(x));
}
});
return result;
}
1、Function.apply只是方便我们自己去转换值
2、MediatorLiveData
对象继承自MutableLiveData
它提供的addSource方法可以观察LiveData对象,这也就是map和switchMap的关键实现。让LiveData可以观察LiveData
3、传入source,被观察者,它一变化就调用onChanged方法。在onChanged里MediatorLiveData设置新的值
4、流程简单总结就是:对象A来观察B的值,根据B的变化,A设置一个新的值,A观察B
@MainThread
public static LiveData switchMap(
@NonNull LiveData source,
@NonNull final Function> switchMapFunction) {
final MediatorLiveData result = new MediatorLiveData<>();
result.addSource(source, new Observer() {
LiveData mSource;
@Override
public void onChanged(@Nullable X x) {
LiveData newLiveData = switchMapFunction.apply(x);
if (mSource == newLiveData) {
return;
}
if (mSource != null) {
result.removeSource(mSource);
}
mSource = newLiveData;
if (mSource != null) {
result.addSource(mSource, new Observer() {
@Override
public void onChanged(@Nullable Y y) {
result.setValue(y);
}
});
}
}
});
return result;
}
1、Function.apply
和MediatorLiveData
和上面map一个用法
2、当第一次x变化,进入onChanged,mSource = newLiveData,MediatorLiveData就会根据新的newLiveData来监听它的变化,addSource持值同步。
3、当第二次x变化,进入onChanged,mSource = newLiveData根据java局部变量特性,newLiveData是不同于第一次x变化时的newLiveData对象。此时会result.removeSource(mSource);移除之前的监听,再重新监听这个新newLiveData的值,并且addSource保持值同步。
4、switchMapFunction的代码从头到尾,result.addSource了两次,也就是有两个监听
5、流程简单总结就是:A观察B的值,B变化必然创建一个新的C,A观察C。所以A同时观察B和C,只不过这个B每次变化会导致A监听一个新的C
6、当然如果你写switchMapFunction.apply(x);
实现的时候,返回的是同一个对象,那么C也不会变。
3.4、合并多个LiveData
合并多个LiveData目的很明确,比如一个按钮需要两个LiveData状态来确定, 你就需要同时监听两个LiveData才行。直接抛出一个例子,很简单就不详解了。
mediatorLiveData.addSource(addressNumber, new Observer() {
@Override
public void onChanged(Object o) {
Address address = (Address)mediatorLiveData.getValue();
address.setNum((Integer)o);
mediatorLiveData.setValue(address);
}
});
mediatorLiveData.addSource(addressString, new Observer() {
@Override
public void onChanged(Object o) {
Address address = (Address)mediatorLiveData.getValue();
address.setString((String) o);
mediatorLiveData.setValue(address);
}
});
mediatorLiveData.observe(this, new Observer() {
@Override
public void onChanged(Object o) {
}
});
class Address {
Integer num;
String string;
...
}
四、写在最后
整体来看LiveData还是一个很简单的控件,它的核心思想 = Lifecycle + 观察者模式。优点离不开它的核心思想,简单来说生命周期有效性不用我们过多担心了,还有就是观察者模式的优点让我们刷新UI变的更便利,能给我们省去不少逻辑代码。所以它还是很巧妙的一个控件。