移动架构46_可观察的数据持有者类-LiveData
Android移动架构汇总
文章目录
-
- 一 什么是LiveData
- 二 LiveData基本使用
-
- 1、添加依赖
- 2、定义
- 3、赋值
- 4、订阅
-
- 1)ViewModel暴露方法
- 2)在Activity或Fragment中中通过observer方法订阅LiveData对象,这样,当LiveData的值改变时,就可以收到更新的通知了
- 三 map与switchMap
-
- 1 map
- 2 switchMap
- 四 原理
一 什么是LiveData
LiveData是一种可观察的数据存储器类,它具有生命周期感知能力,可确保LiveData仅更新处于活跃生命周期的应用组件观察者。
* LiveData is a data holder class that can be observed within a given lifecycle.* This means that an {@link Observer} can be added in a pair with a {@link LifecycleOwner}, and* this observer will be notified about modifications of the wrapped data only if the paired* LifecycleOwner is in active state. LifecycleOwner is considered as active, if its state is* {@link Lifecycle.State#STARTED} or {@link Lifecycle.State#RESUMED}. An observer added via
* {@link #observeForever(Observer)} is considered as always active and thus will be always notified
* about modifications. For those observers, you should manually call* {@link #removeObserver(Observer)}. <p> An observer added with a Lifecycle will be automatically removed if the corresponding* Lifecycle moves to {@link Lifecycle.State#DESTROYED} state. This is especially useful for* activities and fragments where they can safely observe LiveData and not worry about leaks:* they will be instantly unsubscribed when they are destroyed. <p>* In addition, LiveData has {@link LiveData#onActive()} and {@link LiveData#onInactive()} methods* to get notified when number of active {@link Observer}s change between 0 and 1.* This allows LiveData to release any heavy resources when it does not have any Observers that* are actively observing.* <p>* This class is designed to hold individual data fields of {@link ViewModel},* but can also be used for sharing data between different modules in your application* in a decoupled fashion. @param <T> The type of data held by this instance* @see ViewModel
二 LiveData基本使用
1、添加依赖
dependencies {...def lifecycle_version = "2.3.1"implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"...
}
2、定义
LiveData为抽象类,MutableLiveData为LiveData子类,在ViewModel中定义
public class ElecSignatureModel extends ViewModel {private MutableLiveData<ElecSignature> mutableLiveData;public class ElecSignatureModel extends ViewModel {mutableLiveData = new MutableLiveData<>();}}
3、赋值
MutableLiveData数据赋值有两种方式,分别为postValue和setValue。这两种方式的区别在于应用场景有所不同,即当设置数据操作在子线程中时使用前者,在UI线程时则使用后者
public void getElcState() {ElectronicRepository.getInstance().getElcSignatureState(new DisposableCallBack<ElecSignature>() {@Overridepublic void onSubscribe() {loadingLiveData.setValue(true);}@Overridepublic void onDispose() {loadingLiveData.setValue(false);}@Overridepublic void onError(Throwable e) {loadingLiveData.setValue(false);//ToastUtil.show("获取是否签署电子签名约定书请求失败");}@Overridepublic void onSuccess(ElecSignature data) {loadingLiveData.setValue(false);mutableLiveData.setValue(data);}@Overridepublic void onFailed(String msg) {loadingLiveData.setValue(false);mutableLiveData.setValue(null);}});
}
4、订阅
1)ViewModel暴露方法
public LiveData<ElecSignature> getMutableLiveData() {return mutableLiveData;
}
2)在Activity或Fragment中中通过observer方法订阅LiveData对象,这样,当LiveData的值改变时,就可以收到更新的通知了
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);viewModel = ViewModelProviders.of(this).get(ElecSignatureModel.class);binding = DataBindingUtil.setContentView(this, R.layout.activity_elec_signature);binding.navigationBar.setTitle("电子签名约定书");//订阅viewModel.getMutableLiveData().observe(this, new Observer<ElecSignature>() {@Overridepublic void onChanged(@Nullable ElecSignature signature) {//TODO }});.....//请求数据viewModel.getElcState();}
三 map与switchMap
1 map
通过map转化,可以将某种LiveData类型的数据转换为另一种类型。当只需要知道一个对象的成员变量是否发生变化,可以用map
*** Applies the given function on the main thread to each value emitted by {@code source}* LiveData and returns LiveData, which emits resulting values.* <p>* The given function {@code func} will be executed on the main thread.* <p>* Suppose that you have a LiveData, named {@code userLiveData}, that contains user data and you* need to display the user name, created by concatenating the first and the last* name of the user. You can define a function that handles the name creation, that will be* applied to every value emitted by {@code useLiveData}.** <pre>* LiveData<User> userLiveData = ...;* LiveData<String> userName = Transformations.map(userLiveData, user -> {* return user.firstName + " " + user.lastName* });* </pre>** @param source a {@code LiveData} to listen to* @param func a function to apply* @param <X> a type of {@code source} LiveData* @param <Y> a type of resulting LiveData.* @return a LiveData which emits resulting values*/
@MainThread
public static <X, Y> LiveData<Y> map(@NonNull LiveData<X> source,@NonNull final Function<X, Y> func) {final MediatorLiveData<Y> result = new MediatorLiveData<>();result.addSource(source, new Observer<X>() {@Overridepublic void onChanged(@Nullable X x) {result.setValue(func.apply(x));}});return result;
}
2 switchMap
switchMap转化是将从外部如网络层获取的LiveData转化为可观察的方式
/* Creates a LiveData, let's name it {@code swLiveData}, which follows next flow:* it reacts on changes of {@code trigger} LiveData, applies the given function to new value of* {@code trigger} LiveData and sets resulting LiveData as a "backing" LiveData* to {@code swLiveData}.* "Backing" LiveData means, that all events emitted by it will retransmitted* by {@code swLiveData}.* <p>* If the given function returns null, then {@code swLiveData} is not "backed" by any other* LiveData. <p>* The given function {@code func} will be executed on the main thread. <p>* Consider the case where you have a LiveData containing a user id. Every time there's a new* user id emitted, you want to trigger a request to get the user object corresponding to that* id, from a repository that also returns a LiveData.* <p>* The {@code userIdLiveData} is the trigger and the LiveData returned by the {@code* repository.getUserById} is the "backing" LiveData.* <p>* In a scenario where the repository contains User(1, "Jane") and User(2, "John"), when the* userIdLiveData value is set to "1", the {@code switchMap} will call {@code getUser(1)},* that will return a LiveData containing the value User(1, "Jane"). So now, the userLiveData* will emit User(1, "Jane"). When the user in the repository gets updated to User(1, "Sarah"),* the {@code userLiveData} gets automatically notified and will emit User(1, "Sarah").* <p>* When the {@code setUserId} method is called with userId = "2", the value of the {@code* userIdLiveData} changes and automatically triggers a request for getting the user with id* "2" from the repository. So, the {@code userLiveData} emits User(2, "John"). The LiveData* returned by {@code repository.getUserById(1)} is removed as a source. <pre>* MutableLiveData<String> userIdLiveData = ...;* LiveData<User> userLiveData = Transformations.switchMap(userIdLiveData, id ->* repository.getUserById(id)); void setUserId(String userId) {* this.userIdLiveData.setValue(userId);* }* </pre> @param trigger a {@code LiveData} to listen to* @param func a function which creates "backing" LiveData* @param <X> a type of {@code source} LiveData* @param <Y> a type of resulting LiveData*/@MainThreadpublic static <X, Y> LiveData<Y> switchMap(@NonNull LiveData<X> trigger,@NonNull final Function<X, LiveData<Y>> func) {final MediatorLiveData<Y> result = new MediatorLiveData<>();result.addSource(trigger, new Observer<X>() {LiveData<Y> mSource;@Overridepublic void onChanged(@Nullable X x) {LiveData<Y> newLiveData = func.apply(x);if (mSource == newLiveData) {return;}if (mSource != null) {result.removeSource(mSource);}mSource = newLiveData;if (mSource != null) {result.addSource(mSource, new Observer<Y>() {@Overridepublic void onChanged(@Nullable Y y) {result.setValue(y);}});}}});return result;}
四 原理
LiveData的本质是观察者模式
/* Adds the given observer to the observers list within the lifespan of the given* owner. The events are dispatched on the main thread. If LiveData already has data* set, it will be delivered to the observer.* <p>* The observer will only receive events if the owner is in {@link Lifecycle.State#STARTED}* or {@link Lifecycle.State#RESUMED} state (active).* <p>* If the owner moves to the {@link Lifecycle.State#DESTROYED} state, the observer will* automatically be removed.* <p>* When data changes while the {@code owner} is not active, it will not receive any updates.* If it becomes active again, it will receive the last available data automatically.* <p>* LiveData keeps a strong reference to the observer and the owner as long as the* given LifecycleOwner is not destroyed. When it is destroyed, LiveData removes references to* the observer & the owner.* <p>* If the given owner is already in {@link Lifecycle.State#DESTROYED} state, LiveData* ignores the call.* <p>* If the given owner, observer tuple is already in the list, the call is ignored.* If the observer is already in the list with another owner, LiveData throws an* {@link IllegalArgumentException}. @param owner The LifecycleOwner which controls the observer* @param observer The observer that will receive the events*/@MainThreadpublic void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {if (owner.getLifecycle().getCurrentState() == DESTROYED) {// ignorereturn;}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);}
从代码中可以看到,observe方法首先会通过@MainThread当前是否运行在主线程,且LiveData和生命周期相关联,如果想让数据监测变化不受活动状态的影响,可以使用observeForever方法,这样Activity即使不处于活动状态,也可以接收到改变的数据,但当Activity销毁时,一定要主动调用removeObserver方法,否则LiveData会一直存在,这会导致内存泄漏。
observe与LifecycleOwner关联起来存放在LifecycleBoundObserver中,LifecycleBoundObserver实现了LifecycleEventObserver接口,当页面发生改变的时候,程序会走到onStateChanged方法中。具体代码如下
@Overridepublic void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {removeObserver(mObserver);return;}activeStateChanged(shouldBeActive());}
页面被销毁时会调用removeObserver移除观察,所以使用LiveData的observe方法不用担心存在内存泄漏的风险。如果之前的周期与当前不同,则会同步一次状态,并调用activeStateChanged方法,而activeStateChanged方法则会调用dispatchingValue方法分发数据。dispatchingValue方法的代码如下:
private void dispatchingValue(@Nullable ObserverWrapper initiator) {if (mDispatchingValue) {mDispatchInvalidated = true;return;}mDispatchingValue = true;do {mDispatchInvalidated = false;if (initiator != null) {considerNotify(initiator);initiator = null;} else {for (Iterator<Map.Entry<Observer<T>, ObserverWrapper>> iterator =mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {considerNotify(iterator.next().getValue());if (mDispatchInvalidated) {break;}}}} while (mDispatchInvalidated);mDispatchingValue = false;}
通过使用mDispatchingValue变量标记来防止分发相同的内容,通过循环方式遍历所有的观察者,通过considerNotify方法更新数据。
private void considerNotify(ObserverWrapper observer) {if (!observer.mActive) {return;}// Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.//// we still first check observer.active to keep it as the entrance for events. So even if// the observer moved to an active state, if we've not received that event, we better not// notify for a more predictable notification order.if (!observer.shouldBeActive()) {observer.activeStateChanged(false);return;}if (observer.mLastVersion >= mVersion) {return;}observer.mLastVersion = mVersion;//noinspection uncheckedobserver.mObserver.onChanged((T) mData);}
如果观察者已经不属于活动状态,直接返回,并通过比较数据的版本号判断数据是否需要更新。如果需要更新则会回调到observer的onChanged方法中,从而实现在UI层接收数据的回调。
版本号何时更新?看设置数据的setValue方法
/* Sets the value. If there are active observers, the value will be dispatched to them.* <p>* This method must be called from the main thread. If you need set a value from a background* thread, you can use {@link #postValue(Object)} @param value The new value*/@MainThreadprotected void setValue(T value) {assertMainThread("setValue");mVersion++;mData = value;dispatchingValue(null);}
setValue方法时,数据版本号改变,通过dispatching-Value方法进行数据处理,从而实现了LiveData的可观察