首语
ViewModel 以注重生命周期的方式存储和管理界面相关的数据,当数据发生变化时,可通过接口的方式通知页面,但是有很多数据要通知时,需要定义大量的接口显得十分冗余,为此,Jetpack提供了LiveData组件解决这种问题,简化了开发过程。
LiveData 是一种可观察的数据存储器类。它是一个数据的容器,将数据包装起来,使数据成为被观察者,当数据发生变化时,观察者能够获得通知。
LiveData 具有生命周期感知能力,它遵循其他应用组件(如 Activity
、Fragment
或 Service
)的生命周期。这种感知能力确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。
优势
- 确保界面符合数据状态 LiveData 遵循观察者模式。当数据发生变化时,LiveData 会通知观察者对象,无需在每次应用数据发生变化时更新界面。
- 不会发生内存泄漏 LiveData会绑定到 LifeCycle 对象,并在其关联的生命周期遭到销毁后进行自我清理。
- 不会因Activity停止而导致崩溃
如果观察者的生命周期处于非活跃状态(如返回栈中的
Activity
),则它不会接收任何 LiveData 事件。 - 不需要手动处理生命周期 界面组件只是观察相关数据,不会停止或恢复观察。LiveData 将自动管理所有这些操作,因为它在观察时可以感知相关的生命周期状态变化。
- 数据始终保持最新状态
如果生命周期变为非活跃状态,它会在再次变为活跃状态时接收最新的数据。例如,曾经在后台的
Activity
会在返回前台后立即接收最新的数据。 - 适当的配置更改
如果由于配置更改(如设备旋转)而重新创建了
Activity
或Fragment
,它会立即接收最新的可用数据。 - 共享资源 使用单例模式扩展封装LiveData,以便在应用中共享它们。
依赖
代码语言:javascript复制 implementation 'androidx.lifecycle:lifecycle-livedata:2.3.0'
//包含了 viewmodel 和 livedata,lifecycle-extensions 中的 API 已弃用
//implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
使用
LiveData是一个抽象类,不能直接使用,通常使用的是它的直接子类MutableLiveData
。通过LiveData ViewModel 完成一个计数器的例子。
public class HomeViewModel extends ViewModel {
//value字段用MutableLiveData包装,通常在ViewModel类中完成
private MutableLiveData<Integer> value;
public MutableLiveData<Integer> getCurrentValue() {
if (value == null) {
value = new MutableLiveData<>();
value.setValue(0);
}
return value;
}
}
定义完LiveData后,就要利用它来完成ViewModel与页面间的通信。
代码语言:javascript复制 HomeViewModel homeViewModel = new ViewModelProvider(this).get(HomeViewModel.class);
MutableLiveData<Integer> currentValue = homeViewModel.getCurrentValue();
currentValue.observe(this, new Observer<Integer>() {
@Override
public void onChanged(Integer integer) {
textView.setText(String.valueOf(integer));
}
});
button.setOnClickListener(view -> {
homeViewModel.addValue();
});
在页面中,通过LiveData.observe()
对LiveData所包装的数据进行观察,我们也可以通过Livedata的postValue()
或Livedata的setValue()
来完成修改数据,postValue()
用在非UI线程中,setValue()
用在UI线程中。
更新页面的LiveData对象存储在ViewModel中,而不将其存储在Activity
或Fragment
中。
public void addValue() {
if (value.getValue() != null) {
value.setValue(value.getValue() 1);
}
}
通常,LiveData仅在数据发生更改时才发送更新,并且仅发送给活跃观察者。一种例外情况是,观察者从非活跃状态更改为活跃状态时也会收到更新。此外,如果观察者第二次从非活跃状态更改为活跃状态,则只有在自上次变为活跃状态以来值发生了更改时,它才会收到更新。
扩展LiveData
LiveData的优势之一就是共享资源,将LiveData类实现一个自定义单例。
代码语言:javascript复制public class LiveDataUtil extends LiveData<Integer> {
private static LiveDataUtil sInstance;
private LiveDataUtil manager;
private SimplePListener listener = new SimpleListener() {
@Override
public void onPriceChanged(Integer value) {
setValue(value);
}
};
@MainThread
public static LiveDataUtil getInstance(String symbol) {
if (sInstance == null) {
sInstance = new LiveDataUtil(symbol);
}
return sInstance;
}
private LiveDataUtil(String symbol) {
manager = new LiveDataUtil(symbol);
}
@Override
protected void onActive() {
manager.requestUpdates(listener);
}
private void requestUpdates(SimpleListener listener) {
this.listener = listener;
}
@Override
protected void onInactive() {
manager.removeUpdates(listener);
}
private void removeUpdates(SimpleListener listener) {
this.listener = listener;
}
}
在Activity
或Fragment
中使用它获取数据。
LiveDataUtil.getInstance("flag").observe(this, new Observer<Integer>() {
@Override
public void onChanged(Integer integer) {
Log.e("yhj", "onChanged: " integer);
}
});
转换LiveData
计数器的例子中,value的类型是Integer
,而给Textview
设置文本类型是String
,我们需要对其中的类型进行更改,LiveData中提供了Transformations
类来进行LiveData的转换,类似于RxJava的操作符。
//aLiveData监听bLiveData内容的变化,变化时将bLiveData转换为相应的内容并通知aLiveData
liveData = Transformations.map(homeViewModel.getCurrentValue(), String::valueOf);
//aLiveData监听bLiveData内容的变化,变化时从bLiveData获取相应的cLiveData,
//添加到aLiveData的监听列表里,即aLiveData同时监听bLiveData和cLiveData,bLiveData的变化只会switch cLiveData,cLiveData变化才会通知aLiveData
aLiveData=Transformations.switchMap(bLiveData, new Function<Integer, LiveData<String>>() {
@Override
public LiveData<String> apply(Integer input) {
return cLiveData;
}
});
合并多个LiveData
同时监听多个LiveData,只要其中一个LiveData更新则收到通知,通过LiveData的子类MediatorLiveData
来实现。
MediatorLiveData<String> mediatorLiveData = new MediatorLiveData<>();
mediatorLiveData.addSource(liveData1, new Observer<String>() {
@Override
public void onChanged(String s) {
mediatorLiveData.setValue(s);
}
});
mediatorLiveData.addSource(liveData2, new Observer<String>() {
@Override
public void onChanged(String s) {
mediatorLiveData.setValue(s);
}
});
mediatorLiveData.observe(this, new Observer<String>() {
@Override
public void onChanged(String s) {
}
});
LiveData实现事件总线
实现事件总线的方式有许多种,诸如EventBus和RxJava实现的RxBus。可参考之前的两篇文章。这两个框架都需要对生命周期进行特殊处理。LiveData自带生命周期感知能力,它本身可感知数据更新,因此,通过LiveData可以实现一个自带生命周期感知能力的事件总线框架。
代码语言:javascript复制public class LiveDataBus {
private static class Lazy {
static LiveDataBus sLiveDataBus = new LiveDataBus();
}
public static LiveDataBus get() {
return Lazy.sLiveDataBus;
}
private ConcurrentHashMap<String, StickyLiveData> mHashMap = new ConcurrentHashMap<>();
public StickyLiveData with(String eventName) {
StickyLiveData liveData = mHashMap.get(eventName);
if (liveData == null) {
liveData = new StickyLiveData(eventName);
mHashMap.put(eventName, liveData);
}
return liveData;
}
public class StickyLiveData<T> extends LiveData<T> {
private String mEventName;
private T mStickyData;
private int mVersion = 0;
public StickyLiveData(String eventName) {
mEventName = eventName;
}
@Override
public void setValue(T value) {
mVersion ;
super.setValue(value);
}
@Override
public void postValue(T value) {
mVersion ;
super.postValue(value);
}
public void setStickyData(T stickyData) {
this.mStickyData = stickyData;
setValue(stickyData);
}
public void postStickyData(T stickyData) {
this.mStickyData = stickyData;
postValue(stickyData);
}
@Override
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
observerSticky(owner, observer, false);
}
public void observerSticky(LifecycleOwner owner, Observer<? super T> observer, boolean sticky) {
super.observe(owner, new WrapperObserver(this, observer, sticky));
owner.getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
mHashMap.remove(mEventName);
}
}
});
}
private class WrapperObserver<T> implements Observer<T> {
private StickyLiveData<T> mLiveData;
private Observer<T> mObserver;
private boolean mSticky;
//标记该liveData已经发射几次数据了,用以过滤老数据重复接收
private int mLastVersion = 0;
public WrapperObserver(StickyLiveData liveData, Observer<T> observer, boolean sticky) {
mLiveData = liveData;
mObserver = observer;
mSticky = sticky;
//比如先使用StickyLiveData发送了一条数据。StickyLiveData#version=1
//那当我们创建WrapperObserver注册进去的时候,就至少需要把它的version和 StickyLiveData的version保持一致
//用以过滤老数据,否则 岂不是会收到老的数据?
mLastVersion = mLiveData.mVersion;
}
@Override
public void onChanged(T t) {
//如果当前observer收到数据的次数已经大于等于了StickyLiveData发送数据的个数了则return
if (mLastVersion >= mLiveData.mVersion) {
//但如果当前observer它是关心 黏性事件的,则给他。
if (mSticky && mLiveData.mStickyData != null) {
mObserver.onChanged(mLiveData.mStickyData);
}
return;
}
mLastVersion = mLiveData.mVersion;
mObserver.onChanged(t);
}
}
}
}
LiveData的原理
LiveData的observe()
方法接收两个参数,第一个是LifecycleOwner
,第二个是Observer
,源码中Observer
与Activity
的生命周期关联在一起,因此,LiveData能够感知页面的生命周期,也可避免引发的内存泄漏问题。
@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还有一个observeForever()
,和observe()
没有太大区别,主要区别在于,当LiveData发生变化时,无论页面处于什么状态,都能收到通知。因此,使用完以后一定要调用removeObserver()
停止对LiveData的观察,否则会造成内存泄漏。
@MainThread
public void removeObserver(@NonNull final Observer<? super T> observer) {
assertMainThread("removeObserver");
ObserverWrapper removed = mObservers.remove(observer);
if (removed == null) {
return;
}
removed.detachObserver();
removed.activeStateChanged(false);
}