移动架构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)}.
 *
 * 

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. * *

* 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. *

* 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 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>() {
        @Override
        public void onSubscribe() {
            loadingLiveData.setValue(true);
        }

        @Override
        public void onDispose() {
            loadingLiveData.setValue(false);
        }

        @Override
        public void onError(Throwable e) {
            loadingLiveData.setValue(false);
            //ToastUtil.show("获取是否签署电子签名约定书请求失败");
        }

        @Override
        public void onSuccess(ElecSignature data) {
            loadingLiveData.setValue(false);
            mutableLiveData.setValue(data);
        }

        @Override
        public 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>() {
        @Override
        public 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>() {
        @Override
        public 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}.
     * 

* If the given function returns null, then {@code swLiveData} is not "backed" by any other * LiveData. * *

* The given function {@code func} will be executed on the main thread. * *

* 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. *

* The {@code userIdLiveData} is the trigger and the LiveData returned by the {@code * repository.getUserById} is the "backing" LiveData. *

* 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"). *

* 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. * *

     * MutableLiveData userIdLiveData = ...;
     * LiveData userLiveData = Transformations.switchMap(userIdLiveData, id ->
     *     repository.getUserById(id));
     *
     * void setUserId(String userId) {
     *      this.userIdLiveData.setValue(userId);
     * }
     * 
* * @param trigger a {@code LiveData} to listen to * @param func a function which creates "backing" LiveData * @param a type of {@code source} LiveData * @param a type of resulting LiveData */
@MainThread public 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; @Override public 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>() { @Override public 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.
     * 

* The observer will only receive events if the owner is in {@link Lifecycle.State#STARTED} * or {@link Lifecycle.State#RESUMED} state (active). *

* If the owner moves to the {@link Lifecycle.State#DESTROYED} state, the observer will * automatically be removed. *

* 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. *

* 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. *

* If the given owner is already in {@link Lifecycle.State#DESTROYED} state, LiveData * ignores the call. *

* 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 */ @MainThread public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) { 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); }

从代码中可以看到,observe方法首先会通过@MainThread当前是否运行在主线程,且LiveData和生命周期相关联,如果想让数据监测变化不受活动状态的影响,可以使用observeForever方法,这样Activity即使不处于活动状态,也可以接收到改变的数据,但当Activity销毁时,一定要主动调用removeObserver方法,否则LiveData会一直存在,这会导致内存泄漏。
observe与LifecycleOwner关联起来存放在LifecycleBoundObserver中,LifecycleBoundObserver实现了LifecycleEventObserver接口,当页面发生改变的时候,程序会走到onStateChanged方法中。具体代码如下

 @Override
        public 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 unchecked
        observer.mObserver.onChanged((T) mData);
    }

如果观察者已经不属于活动状态,直接返回,并通过比较数据的版本号判断数据是否需要更新。如果需要更新则会回调到observer的onChanged方法中,从而实现在UI层接收数据的回调。

版本号何时更新?看设置数据的setValue方法

/**
     * Sets the value. If there are active observers, the value will be dispatched to them.
     * 

* 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 */ @MainThread protected void setValue(T value) { assertMainThread("setValue"); mVersion++; mData = value; dispatchingValue(null); }

setValue方法时,数据版本号改变,通过dispatching-Value方法进行数据处理,从而实现了LiveData的可观察

你可能感兴趣的:(Android移动架构,Jetpack,mvvm,LiveDataa,MutableLiveData,数据观察生命周期)