上一篇文章ViewModel简单使用与解析我们说了ViewModel相关的知识,接下来我们要说的是ViewModel的终极搭档LiveData。
先来说一下LiveData的优点:
- 减少内存泄漏,不需要手动处理生命周期,
LiveData会绑定实现了LifecycleOwner接口的对象,并会在这些对象被销毁后自行清理。
Activity/Fragment都继承并实现了这个接口,所以我们只需对数据进行订阅便可,不需要手动 去停止或恢复观察。(也可以使用observeForever(Observer)方法注册一个没有关联LifecycleOwner的对象,但是这得手动调用removeObserver(Observer)方法进行移除) - 在最合适的时候通知更新数据
LiveData只会将数据通知给处于活跃状态下的对象,因此可以规避很多异步任务结束后更新已经被回收的UI而造成的空指针异常,而非活跃状态下的对象当基状态变成活跃时也会收到最新的数据。 - 正确应对Configuration Change
如果一个Activity或Fragment由于Configuration Change更改(如设备旋转)而重新创建,它会立即收到最新的可用数据。 - 共享资源
您可以使用单例模式扩展LiveData对象并包装成系统服务,以便在应用程序中进行共享。LiveData对象一旦连接到系统服务,任何需要该资源的Observer都只需观察这个LiveData对象。 有关更多信息,请参阅扩展LiveData。
1.创建LiveData对象
public class MViewModel extends ViewModel {
MutableLiveData mString;
public MutableLiveData getString(){
if(mString==null){
mString=new MutableLiveData<>();
}
return mString;
}
public MutableLiveData getMsgString(){
if(msgString==null){
msgString=new MutableLiveData<>();
}
return msgString;
}
}
一般都是在ViewModel中声明和保存LiveData,这有利于:
- 保持Activity/Fragment的精简性,这些UI控制器负责显示数据而不是保存数据状态。
- 将LiveData实例与特定Activity/Fragment实例分离,这将使得LiveData对象在发生Configuration Change后仍然存活。
2.订阅LiveData对象
官方推荐在应用程序组件的onCreate()方法开始观察LiveData对象,理由是:
- 确保系统不会从Activity或Fragment的onResume()方法中进行多余的调用。
- 确保Activity或Fragment一旦变为活动状态时,就有可展示的数据。 当应用程序组件处于STARTED状态,它就需从它所观察的LiveData对象中接收到最新的值。 所以我们需要在一开始就设置好订阅。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView=findViewById(R.id.desc);
mViewModel = ViewModelProviders.of(this).get(MViewModel.class);
mViewModel.getString().observe(this, new Observer() {
@Override
public void onChanged(String s) {
Log.e("MainActivity", "耗时任务结束返回数据");
mTextView.setText(s);
}
});
3.更新LiveData对象
一般在ViewModel中对数据进行处理,当我们拿到数据后将值赋值给LiveData
public void startTask(){
new Thread(){
@Override
public void run() {
//请求网络数据、数据库、加载大图等。
//如果在Activity转屏的时候取消这些任务,那恢复的时候就要重新加载,势必浪费资源
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//更新LiveData
mString.postValue("我是来自3秒后的数据");
super.run();
}
}.start();
}
setValue()或者postValue()都可以用来更新LiveData的值,区别在于:
setValue要在主线程中使用,postValue在后台线程中使用。
4.LiveData Transformations
有时候我们想在ViewModel中对LiveData进行数据转换之后再输出给observer,比如我我拿到了用户ID,我们需要通过ID拿到用户信息再去更新UI,那我们就可以用Transformations来对一个LiveData对象进行转换,Transformations类目前只提供两个转换方法map()和switchMap():
static LiveData map(LiveData source,Function mapFunction) {}
static LiveData switchMap(LiveData source,Function> switchMapFunction){}
简单对比一下你会发现这两个方法只是最后一个传入的参数不同而已,返回结果跟第一个参数是一样的,至于选择哪个方法,就完全是看你要传入的参数类型了
LiveData userId...;
LiveData userBean=Transformations.map(userId, new Function() {
@Override
public UserBean apply(Long input) {
//比如说你刚好有个方法是通过userId获取到userBean,并且是返回UserBean类型的就用map
return new UserBean();
}
});
LiveData userBean=Transformations.switchMap(userId, new Function>() {
@Override
public LiveData apply(Long input) {
//比如说你刚好有个方法是通过userId获取到userBean,并且是返回LiveData类型的就用switchMap
return new MutableLiveData(){};
}
});
再来看下该类完整的代码
public class Transformations {
private Transformations() {
}
@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;
}
@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;
}
}
你会发现其实就是通过类把LiveData
5. MediatorLiveData
我们前面有提到过,只有LifecycleOwner对象才能使用observer方法对LiveData进行监听,而Transformations的转换思路显然是通过对上一个LiveData进行了监听然后在其值发生变化的时候封装成MediatorLiveData对象输出。所以应该是用到了LiveData的observeForever方法。
我们先来看下它的addSource方法
public void addSource(@NonNull LiveData source, @NonNull Observer super S> onChanged) {
Source e = new Source<>(source, onChanged);
Source> existing = mSources.putIfAbsent(source, e);
if (existing != null && existing.mObserver != onChanged) {
throw new IllegalArgumentException(
"This source was already added with the different observer");
}
if (existing != null) {
return;
}
if (hasActiveObservers()) {
e.plug();
}
}
传入的LiveData和Observer被用来构造Source对象,看下Source代码
private static class Source implements Observer {
final LiveData mLiveData;
final Observer super V> mObserver;
int mVersion = START_VERSION;
Source(LiveData liveData, final Observer super V> observer) {
mLiveData = liveData;
mObserver = observer;
}
void plug() {
//此处完成了对传入的LiveData的订阅
mLiveData.observeForever(this);
}
void unplug() {
mLiveData.removeObserver(this);
}
@Override
public void onChanged(@Nullable V v) {
if (mVersion != mLiveData.getVersion()) {
mVersion = mLiveData.getVersion();
mObserver.onChanged(v);
}
}
}
果然是用到了observeForever对传入的LiveData进行了监听,然后在onChanged方法将值继续传递下去。而MediatorLiveData可以多少调用addSource,其内部用了一个Map来存储构造生成的Source对象。
MediatorLiveData相当于一个自定义的LiveData,其内部定义了一个Map用于存储
private SafeIterableMap, Source>> mSources = new SafeIterableMap<>();
所以MediatorLiveData除了用于上面Transformations的转换操作之外还可用来做合并订阅的操作。
* LiveData liveData1 = ...;
* LiveData liveData2 = ...;
*
* MediatorLiveData liveDataMerger = new MediatorLiveData<>();
* liveDataMerger.addSource(liveData1, value -> liveDataMerger.setValue(value));
* liveDataMerger.addSource(liveData2, value -> liveDataMerger.setValue(value));
这样只要是liveData1、liveData2其中一个数据的变化都会引发liveDataMerger去发出更新数据的通知。