上篇文章《万物基于Lifecycle》 介绍了整个Lifecycle体系的基石,今天这篇文章我们来看看Jetpack给咱们带来的活着的数据——LiveData。java
大纲android
一. LiveData 是什么?数据库
LiveData 简单来讲,就是普通数据对象的一个包装类,这个包装类中帮助你主动管理了数据的版本号,基于观察者模式,让普通的数据对象可以感知所属宿主(Activity、Fragment)的生命周期。这种感知能力就可以保证只有宿主活跃(Resumed、Started)时,数据的观察者才能受到数据变化的消息。api
上面这短短的一段话,却有着重大的意义,举一个case:服务器
有一个页面须要加载一个列表,咱们须要在后台线程去服务器请求对应的数据,请求数据成功并通过解析后post消息通知UI,UI再渲染请求来的数据。等等!倘若此时的网络很慢,而恰好用户此时按home键退出了应用,那么在此时UI是不该该被渲染的,而是应该等用户从新进入应用时才开始渲染。网络
从下面的演示代码就诠释了上面的说所的case架构
class MainActivity extends AppcompactActivity{ public void onCreate(Bundle bundle){ Handler handler = new Handler(){ @Override public void handleMessage(@NonNull Message msg) { //不管页面可见不可见,都会去执行页面刷新,IO。更有甚者弹出对话框 } }; //1.不管当前页面是否可见,这条消息都会被分发。----消耗资源 //2.不管当前宿主是否还存活,这条消息都会被分发。---内存泄漏 handler.sendMessage(msg) liveData.observer(this,new Observer<User>){ void onChanged(User user){ } } //1.减小资源占用--- 页面不可见时不会派发消息 //2.确保页面始终保持最新状态---页面可见时,会马上派发最新的一条消息给全部观察者--保证页面最新状态 //3.再也不须要手动处理生命周期---避免NPE //4.能够打造一款不用反注册,不会内存泄漏的消息总线---取代eventbus liveData.postValue(data); } }
有人说,我能够在处理消息时,根据当前页面时是否可见来具体处理对应逻辑。是的,没错,确实能够这样,就像下面这样。app
Handler handler = new Handler(){ @Override public void handleMessage(@NonNull Message msg) { if (isActivityValid()) { // updateUI... } else { // dosomething... } } };
我再拿上面的例子说一下这种问题:ide
上面的例子已经很好地诠释了LiveData的强大,固然不只限于此,它的优点以下:工具
确保界面符合数据状态
LiveData 遵循观察者模式。当生命周期状态发生变化时,LiveData 会通知 Observer
对象。观察者能够在onChanged事件时更新界面,而不是在每次数据发生更改时当即更新界面。
不会发生内存泄漏
观察者会绑定到 Lifecycle
对象,并在其关联的生命周期遭到销毁后进行自我清理。
不会因 Activity 中止而致使崩溃
若是观察者的生命周期处于非活跃状态(如返回栈中的 Activity),则它不会接收任何 LiveData 事件。
再也不须要手动处理生命周期
界面组件只是观察相关数据,不会中止或恢复观察。LiveData 将自动管理全部这些操做,由于它在观察时能够感知相关的生命周期状态变化。
数据始终保持最新状态
若是生命周期变为非活跃状态,它会在再次变为活跃状态时接收最新的数据。例如,曾经在后台的 Activity 会在返回前台后当即接收最新的数据。
适当的配置更改
若是因为配置更改(如设备旋转)而从新建立了 Activity 或 Fragment,它会当即接收最新的可用数据。
共享资源
可使用单一实例模式扩展 LiveData
对象以封装系统服务,以便在应用中共享它们。LiveData
对象链接到系统服务一次,而后须要相应资源的任何观察者只需观察 LiveData
对象
支持黏性事件的分发
即先发送一条数据,后注册一个观察者,默认是可以收到以前发送的那条数据的
step1: 添加依赖:
step2: 在ViewModel中建立 LiveData
实例 (ViewModel组件会在下期讲到,将LiveData存储至viewModel中,是为了符合MVVM架构思想,V层仅负责展现,而VM层负责数据逻辑)
public class ViewModelTest extends AndroidViewModel { public final MutableLiveData<String> name = new MutableLiveData<>(); ... }
step3: 在Activity或Fragment中对LiveData进行添加观察者进行监听
public class ActivityTest extends AppCompatActivity { ... @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... viewModel.name.observe(this, new Observer<String>() { @Override public void onChanged(@Nullable String name) { // dosomething... } }); } }
咱们能够根据LiveData值的变化来作对应的事情,且不用担忧生命周期越界的问题。
方法名 | 做用 |
---|---|
observe(LifecycleOwner owner,Observer observer) | 注册和宿主生命周期关联的观察者 |
observeForever(Observer observer) | 注册观察者,不会反注册,需自行维护 |
setValue(T data) | 发送数据,没有活跃的观察者时不分发。只能在主线程。 |
postValue(T data) | 和setValue同样。不受线程环境限制, |
onActive | 当且仅当有一个活跃的观察者时会触发 |
inActive | 不存在活跃的观察者时会触发 |
该类十分简单,主要开放了LiveData的发消息接口
public class MutableLiveData<T> extends LiveData<T> { @Override public void postValue(T value) { super.postValue(value); } @Override public void setValue(T value) { super.setValue(value); } }
设计初衷:考虑单一开闭原则,LiveData只能接受消息,避免拿到LiveData对象既能发消息也能收消息的混乱使用。
合并多个LiveData, 即一对多统一观察,一个经典的场景是:在向服务器请求数据时,优先展现本地数据库的数据,而后由请求的响应决定是否要更新数据库,以下图所示:
// ResultType: Type for the Resource data. // RequestType: Type for the API response. public abstract class NetworkBoundResource<ResultType, RequestType> { // MediatorLiveData 数据组合者 private final MediatorLiveData<Resource<ResultType>> result = new MediatorLiveData<>(); private Executor executor; @MainThread protected NetworkBoundResource(Executor mExecutor) { this.executor = mExecutor; // 首先初始化一个Loading的status 空result result.setValue(Resource.loading(null)); // 而后从数据库中获取持久化数据 LiveData<ResultType> dbSource = loadFromDb(); // 数据组合者监听数据库中的数据 result.addSource(dbSource, data -> { // dbSource第一次回调,用来判断数据有效期,此时取消监听 result.removeSource(dbSource); // 业务自行定义是否须要fetch最新的数据 if (shouldFetch(data)) { fetchFromNetwork(dbSource); } else { // 数据有效,从新观察一次,观察者会立马收到一次回调(LiveData粘性事件机制) result.addSource(dbSource, newData -> result.setValue(Resource.success(newData))); } }); } private void fetchFromNetwork(final LiveData<ResultType> dbSource) { LiveData<ApiResponse<RequestType>> apiResponse = createCall(); // 这里数据虽无效,可是能够先给UI展现 result.addSource(dbSource, newData -> setValue(Resource.loading(newData))); result.addSource(apiResponse, response -> { result.removeSource(apiResponse); result.removeSource(dbSource); if (response != null) { if (response.isSuccessful()) { executor.execute(() -> { saveCallResult(processResponse(response)); executor.execute(() -> // 这里咱们拿到的最新的数据须要主动通知监听者,以拿到从服务端拿到的最新数据 result.addSource(loadFromDb(), newData -> setValue(Resource.success(newData))) ); }); } else { onFetchFailed(); result.addSource(dbSource, newData -> setValue(Resource.error(response.errorMessage, newData))); } } else { result.addSource(dbSource, newData -> setValue(Resource.error("Request failed, server didn't response", newData))); } }); } }
上面的例子MediatorLiveData同时监听了数据库中的LiveData和服务端的LiveData
这是一个数据转化工具类,共两个主要方法:
静态转化 -- map(@NonNull LiveData
MutableLiveData<Integer> data = new MutableLiveData<>(); //数据转换 LiveData<String> transformData = Transformations.map(data, input -> String.valueOf(input)); //使用转换后生成的transformData去观察数据 transformData.observe( this, output -> { }); //使用原始的livedata发送数据 data.setValue(10);
动态转化 -- LiveData
@NonNull LiveData
@NonNull final Function<X, LiveData
MutableLiveData userIdLiveData = ...; // 用户数据和用户id紧密相关,当咱们改变userId的liveData的同时还会主动通知userLiveData更新 LiveData userLiveData = Transformations.switchMap(userIdLiveData, id -> repository.getUserById(id)); void setUserId(String userId) { this.userIdLiveData.setValue(userId); } // 不要像下面这么作 private LiveData getUserLiveData(String userId) { // DON'T DO THIS return repository.getUserById(userId); }
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) { //1. 首先来个断言,这个方法只能在主线程调用,observeForever也是。 assertMainThread("observe"); //2.其次把注册进来的observer包装成 一个具备生命周边边界的观察者 //它能监听宿主被销毁的事件,从而主动的把本身反注册,避免内存泄漏 //此时观察者是否处于活跃状态就等于宿主是否可见 LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer); //3.接着会判断该观察是否已经注册过了,若是是则抛异常,因此要注意,不容许重复注册 ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper); if (existing != null && !existing.isAttachedTo(owner)) { throw new IllegalArgumentException("Cannot add the same observer" + " with different lifecycles"); } //4.这一步才是关键 //利用Lifecycle,把观察者注册到进去,才能监听到宿主生命周期状态的变化,对不对? //根据Lifecycle文章中的分析,一旦一个新的观察者被添加,Lifecycle也会同步它的状态和宿主一致对不对?此时会触发观察者的onStateChanged方法 owner.getLifecycle().addObserver(wrapper); }
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver { LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) { super(observer); } @Override boolean shouldBeActive() { //使用observer方法注册的观察者都会被包装成LifecycleBoundObserver //观察者是否活跃就等于宿主 的状态是否大于等于STARTED, //若是页面当前不可见,你发送了一条消息,此时是不会被分发的,能够避免后台任务抢占资源,当页面恢复可见才会分发。 //注意:若是使用observerForever注册的观察者, //会被包装成AlwaysActiveObserver,它的shouldBeActive一致返回true.即使在页面不可见也能收到数据 return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED); } @Override public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) { //在这里若是监听到宿主被销毁了,则主动地把本身从livedata的观察者中移除掉 if (mOwner.getLifecycle().getCurrentState() == DESTROYED) { removeObserver(mObserver); return; } //不然说明宿主的状态发生了变化,此时会判断宿主是否处于活跃状态 activeStateChanged(shouldBeActive()); } }
abstract class ObserverWrapper{ final Observer<? super T> mObserver; boolean mActive; int mLastVersion = START_VERSION//这里就等于-1,没有主动和LiveData的mVersion对齐,为黏性事件埋下了伏笔 void activeStateChanged(boolean newActive) { if (newActive == mActive) { return; } //更改观察者的状态 mActive = newActive; boolean wasInactive = LiveData.this.mActiveCount == 0; //若是此时有且只有一个活跃的观察者则触发onActive LiveData.this.mActiveCount += mActive ? 1 : -1; if (wasInactive && mActive) { onActive(); } //没有任何一个活跃的观察者则触发onInactive //利用这个方法被触发的时机,能够作不少事,好比懒加载,资源释放等 if (LiveData.this.mActiveCount == 0 && !mActive) { onInactive(); } //若是此时观察者处于活跃状态,下面就开始分发数据了 //请注意,这里传递了this = observer if (mActive) { dispatchingValue(this); } } }
void dispatchingValue(@Nullable ObserverWrapper initiator) { if (mDispatchingValue) { mDispatchInvalidated = true; return; } mDispatchingValue = true; do { mDispatchInvalidated = false; if (initiator != null) { //若是传递的观察者不为空,则把数据分发给他本身。这个流程是新注册观察者的时候会被触发 considerNotify(initiator); initiator = null; } else { //不然遍历集合中全部已注册的的观察者,逐个调用considerNotify,分发数据 for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator = mObservers.iteratorWithAdditions(); iterator.hasNext(); ) { considerNotify(iterator.next().getValue()); if (mDispatchInvalidated) { break; } } } } while (mDispatchInvalidated); mDispatchingValue = false; }
private void considerNotify(ObserverWrapper observer) { //观察者当前状态不活跃不分发 if (!observer.mActive) { return; } //观察者所在宿主是否处于活跃状态,不然不分发,而且更改观察者的状态为false if (!observer.shouldBeActive()) { observer.activeStateChanged(false); return; } //此处判断观察者接收消息的次数是否大于等于 发送消息的次数 //可是observer被建立之初verison=-1 //若是此时LiveData已经发送过数据了。这里就不知足了,就出现黏性事件了,后注册的观察者收到了前面发送的消息。 if (observer.mLastVersion >= mVersion) { return; } //每分发一次消息,则把观察者和LiveData的version对齐,防止重复发送 observer.mLastVersion = mVersion; //最后的数据传递 observer.mObserver.onChanged((T) mData); }
普通消息分发流程。即调用 postValue,setValue 才会触发消息的分发:
Android开发大部分主要的工做就是从服务器获取数据,将其转为渲染为UI展现给用户。本质上咱们所作的逻辑都是“数据驱动”,全部的View都是数据的一种状态,数据映射的过程当中咱们须要去考虑生命周期的限制--即数据的活跃性。LiveData结合Lifecycle帮咱们规范了这个流程,完美诠释了observer、lifecycle-aware、data holder 这个铁三角,开发者在遵循这个开发流程的过程当中,便完成了UI -> ViewModel -> Data的单项依赖。