LiveData 是一个可以被观察的数据持有类,它可以感知 Activity、Fragment或Service 等组件的生命周期。简单来说,他主要有一下优点。
它可以做到在组件处于激活状态的时候才会回调相应的方法,从而刷新相应的 UI。
不用担心发生内存泄漏
当 config 导致 activity 重新创建的时候,不需要手动取处理数据的储存和恢复。它已经帮我们封装好了。
当 Actiivty 不是处于激活状态的时候,如果你想 livedata setValue 之后立即回调 obsever 的 onChange 方法,而不是等到 Activity 处于激活状态的时候才回调 obsever 的 onChange 方法,你可以使用 observeForever 方法,但是你必须在 onDestroy 的时候 removeObserver。
回想一下,在你的项目中,是不是经常会碰到这样的问题,当网络请求结果回来的时候,你经常需要判断 Activity 或者 Fragment 是否已经 Destroy, 如果不是 destroy,才更新 UI。而当你如果使用 Livedata 的话,因为它是在 Activity 处于 onStart 或者 onResume 的状态时,他才会进行相应的回调,因而可以很好得处理这个问题,不必写一大堆的 activity.isDestroyed()。接下来,让我们一起来看一下 LiveData 的使用。
LiveData 是一个抽象类,它的实现子类有 MutableLiveData ,MediatorLiveData。在实际使用中,用得比较多的是 MutableLiveData。他常常结合 ViewModel 一起使用。下面,让我们一起来看一下怎样使用它?
首先,我们先写一个类继承我们的 ViewModel,里面持有 mNameEvent。
接着,我们在 Activity 中创建 ViewModel,并监听 ViewModel 里面 mNameEvent 数据的变化,当数据改变的时候,我们打印相应的 log,并设置给 textView,显示在界面上。这样我们就完成了对 mNameEvent 数据源的观察。
LiveData原生的API提供了2种方式供开发者更新数据, 分别是setValue()和postValue(),官方文档明确标明:setValue()方法必须在主线程进行调用,而postValue()方法更适合在执行较重工作子线程中进行调用(比如网络请求等)——在所有情况下,调用setValue()或postValue()都会触发观察者并更新UI。
同样是调用 ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) 方法,只不过,需要多传递一个 factory 参数。
Factory 是一个接口,它只有一个 create 方法。
在实际当中,我们的做法是:实现 Factory 接口,重写 create 方法,在create 方法里面调用相应的构造函数,返回相应的实例。
我们都知道,RxJava在使用过程中,避免内存泄漏是一个不可忽视的问题,因此我们一般需要借助三方库比如RxLifecycle、AutoDispose来解决这个问题。
而反观LiveData,当它被我们的Activity订阅观察,这之后Activity如果finish()掉,LiveData本身会自动“清理”以避免内存泄漏。
这是一个非常好用的特性,它的实现原理非常简单,其本质就是利用了Jetpack 架构组件中的另外一个成员——Lifecycle。
自定义LiveData
LiveData是一个用于持有数据并支持数据可被监听(观察)。和传统的观察者模式中的被观察者不一样,LiveData是一个生命周期感知组件,因此观察者可以指定某一个LifeCycle给LiveData,并对数据进行监听。
如果观察者指定LifeCycle处于Started或者RESUMED状态,LiveData会将观察者视为活动状态,并通知其数据的变化。
onActive()
当这个方法被调用时,表示LiveData的观察者数量从0变为了1,这时就我们的位置监听来说,就应该注册我们的时间监听了。
onInactive()
这个方法被调用时,表示LiveData的观察者数量变为了0,既然没有了观察者,也就没有理由再做监听,此时我们就应该将位置监听移除。
setValue() 更新LiveData的值,并通知到观察者
除了使用MutableLiveData外,我们还可以通过继承LiveData类来扩展一些功能。
比如:只有观察者观察了LiveData的数据时,才开始进行进行获取数据的操作,这样可以节省资源,代码如下所示:
public class TestLiveData extends LiveData
private static TestLiveData sInstance;
private LocationUtil mLocationUtil;
//设计为单例模式
public static TestLiveData getInstance() {
if (sInstance == null) {
sInstance = new TestLiveData();
}
return sInstance;
}
private TestLiveData() {
//创建一个获取位置的对象
mLocationUtil = new LocationUtil();
}
@Override
protected void onActive() {
//开始获取位置信息
mLocationUtil.start(mLocationListener);
}
@Override
protected void onInactive() {
//停止获取位置信息
mLocationUtil.stop();
}
//创建一个位置监听器
private LocationListener mLocationListener = new LocationListener() {
@Override
public void onReceiveLocation(String location) {
//接受到位置信息后,更新数据
setValue(location);
}
};
}
将LiveData设计成单例模式后,可以在多个Activity、Fragment和Service之间共享它。
下面我们来重点关系一下LiveData的三个方法:onActive()、onInactive()、setValue()。
onActive():当有一个处于活跃状态的观察者监听LiveData时会被调用,这表示开始获取位置信息。
onInactive():当没有任何处于活跃状态的观察者监听LiveData时会被调用。由于没有观察者在监听了,所以也没必要继续去获取位置信息了,这只会消耗更多的电量等等,因此就可以停止获取位置信息了。
setValue():更新LiveData的值,并通知到观察者。
下面来看下怎么使用这个自定义的LiveData:
public class TestActivity extends AppCompatActivity {
private static final String TAG = "test";
private TextView mTextView;
private TestLiveData mTestLiveData;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test_activity);
mTextView = findViewById(R.id.tv_test);
initVariable();
}
private void initVariable() {
final Observer
@Override
public void onChanged(@Nullable final String newName) {
Log.d(TAG, "onChanged: " + newName);
mTextView.setText(newName);
}
};
//通过单例获取对象
mTestLiveData =TestLiveData.getInstance();
//同样是去观察LiveData
mTestLiveData.observe(this, statusObserver);
}
}
跟之前的一样,还是通过一个观察者去监听这个mTestLiveData对象。
LiveData变换主要有两种变换:map和switchMap,都是Transformations类提供的。
public class TestViewModel extends ViewModel {
private MutableLiveData
//通过Transformations.map()将Integer类型的值转换为String类型
private LiveData
public MutableLiveData
return mNumLiveData;
}
public LiveData
return mStrLiveData;
}
}
map变换可以直接修改返回的值和类型。
public class TestViewModel extends ViewModel {
private MutableLiveData
//switchMap变换
private LiveData
//返回一个LiveData
private LiveData
MutableLiveData
liveData.setValue(num + "a");
return liveData;
}
public MutableLiveData
return mNumLiveData;
}
public LiveData
return mNameLiveData;
}
}
switchMap变换需要返回一个LiveData对象,这就是跟map变换的区别。
如果有多个LiveData,可以使用MediatorLiveData来合并这些LiveData,一旦其中一个LiveData发生变化,MediatorLiveData都会通知观察者。比如:一个UI界面,依赖于网络数据和数据库,因此就会存在两个LiveData。使用MediatorLiveData将两个LiveData合并后,UI界面只需要观察一个MediatorLiveData即可。当其中一个LiveData数据发生变化时都会通知UI界面去更新。
MediatorLiveData的简单使用如下所示:
LiveData liveData1 = ...;
LiveData liveData2 = ...;
MediatorLiveData liveDataMerger = new MediatorLiveData<>();
liveDataMerger.addSource(liveData1, value -> liveDataMerger.setValue(value));
liveDataMerger.addSource(liveData2, value -> liveDataMerger.setValue(value));
然后在UI界面直接观察这个liveDataMerger即可。
MediatorLiveData相比于LiveData,主要是多了以下两个方法:
addSource():添加源LiveData,并且开始监听给定的源LiveData,当源LiveData的数据发生变化时,观察者的onChanged()方法将会被调用,前提是MediatorLiveData处于活跃状态。
removeSource():移除LiveData。
再来看个例子:
liveDataMerger.addSource(liveData1, new Observer() {
private int count = 1;
@Override public void onChanged(@Nullable Integer s) {
count++;
liveDataMerger.setValue(s);
if (count > 10) {
liveDataMerger.removeSource(liveData1);
}
}
});
监听liveData1的数据,当监听了10次之后,移除对liveData1监听。
MediatorLiveData除了可以合并数据外,实际上还可以用来实现自定义的变换方法,上面Transformations的map()和switchMap()如果不能满足变换需求的话,那么就可以用MediatorLiveData来实现。