概念
在JetPack学习笔记之ViewModel中,当数据发生变化时,我们是可以采用接口的方式实现对页面的通知的,比如将计数显示到Activity中。通过接口的方式对页面进行通知是可行的,但如果要观察的数据很多,则需要定义大量的接口,代码显得十分冗余。为此,Jetpack提供了LiveData组件。
LiveData是一个可被观察的数据容器类。具体来说,可以将LiveData理解为一个数据的容器,它将数据包装起来,是数据成为被观察者,当该数据发生变化时,观察者能够获得通知。我们不需要自己去实现观察者模式,LiveData内部已经默认实现好了,我们只要使用就可以了。
LiveData与ViewModel之间的关系
ViewModel用于存放页面所需的各种数据,还可以在其中放一些与数据相关的逻辑。例如,我们可以在ViewModel中进行数据的加工、获取等操作。
对页面来说,它只关心需要展示的数据是什么,并且希望在数据发生变化时得到通知并能及时的做出更新。
LiveData的作用就是,在ViewModel中的数据发生变化时通知页面。因此,LiveData通常被放在ViewModel中使用,用于包装ViewModel中那些需要被外界观察的数据。
使用
LiveData是一个抽象类,不能直接使用。通常我们使用的是它的直接子类MutableLiveData。
public class MyViewModel extends ViewModel {
private Timer mTimerTask;
private int mCount;
private MutableLiveData<Integer> mLiveCount;// LiveData
public MyViewModel() {
Log.e("MyViewModel", "MyViewModel 创建了");
}
public MutableLiveData<Integer> getLiveCount(){// 获取LiveData
if (mLiveCount == null) {
mLiveCount = new MutableLiveData<>();
}
return mLiveCount;
}
public void startTimer() {
if (mTimerTask == null) {
mTimerTask = new Timer();
}
mTimerTask.schedule(new TimerTask() {
@Override
public void run() {
Log.e("MyViewModel", "mCount -> " + mCount++);
mLiveCount.setValue(mCount);// 更新数据
}
}, 1000, 1000);
}
@Override
protected void onCleared() {
super.onCleared();
Log.e("MyViewModel", "onCleared");
if (mTimerTask != null) {
mTimerTask.cancel();
}
}
}
如何完成页面与ViewModel间的通信呢?
MyViewModel mMyViewModel = new ViewModelProvider(this).get(MyViewModel.class);
mMyViewModel.getLiveCount().observe(this, new Observer<Integer>() {
@Override
public void onChanged(Integer integer) {
mTvCount.setText("计时器 -> " + integer);
}
});
mMyViewModel.startTimer();
在页面中,通过LiveData.oberve方法对LiveData所包装的数据进行观察。反过来,当我们希望修改LiveData所包装的数据时,也可以通过LiveData.postValue/LiveData.setValue方法来完成。
- postValue:在非UI线程中。
- setValue:若在UI线程中,则使用setValue方法。
LiveData的原理
LiveData的源码量也不多,快速理解LiveData的原理(如需深入理解,推荐品尝全部源码),可以关注LiveData的核心方法 - observe方法:
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
assertMainThread("observe");
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
return;
}
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
// 异常拦截
if (existing != null && !existing.isAttachedTo(owner)) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if (existing != null) {
return;
}
// 核心代码 - 感知页面生命周期
owner.getLifecycle().addObserver(wrapper);
}
可以看到,方法的最后一行与页面的生命周期关联在一起了。因此,LiveData可以感知页面的生命周期,它可以检测页面当前的状态是否为激活状态,或者页面是否被销毁。只有在页面处于激活状态(LifeCycle.State.ON_STARTED 或 LifeCycle.State.ON_RESUME)时,页面才能收到来自LiveData的通知,若页面被销毁了(LiveData.State.ON_DESTORY),那么LiveData会自动清除与页面的关联,从而避免可能引发的内存泄露问题。
LiveData.observeForever
该方法使用起来与observe方法没太大的区别,与observe的主要不同是:
当LiveData所包装的数据发生变化时,无论页面处于什么状态,observeForever都能收到通知。因此,用完之后,一定要记得调用removeObserver方法来停止对LiveData的观察,否则LiveData将一直处于激活状态,Activity则永远不会被系统回收,这就造成了内存泄露了。
总结
LiveData的本质是观察者模式,并且它能感知页面的生命周期,只有在页面存活时才会进行通知,从而避免内存泄露。当然你也可以使用observeForever方法让LiveData忽略页面的生命周期,但是用完之后,一定要记得使用removeObserver方法移除监听,否则会造成内存泄露。
LiveData大部分时候是在ViewModel中使用的。但它的作用远不止于此,LiveData在其他地方也能发挥重要作用。Jetpack在设计之初充分考虑了组件配合使用的需求。例如Room + LiveData。