Android Jetpack架构组件——LiveData原理篇

2024-02-29 07:58

本文主要是介绍Android Jetpack架构组件——LiveData原理篇,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

前面我们讲到了LiveData是如何使用的,并在最后留了几个问题。比如它是如何通过生命周期去变化的?为什么DESTORY不会接受数据?postValue和setValue是如何更新数据的?Transformations的map和switchMap方法内部是如何操作的?别急,本篇文章会带你了解其原理。接下来上正文。

LiveData是如何观察生命周期变化的?

如何观察生命周期,我们需要通过他的observe方法去看,我们上源码:

    @MainThreadpublic void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {assertMainThread("observe");//1if (owner.getLifecycle().getCurrentState() == DESTROYED) {// ignorereturn;}//2LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);//3ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);//4if (existing != null && !existing.isAttachedTo(owner)) {throw new IllegalArgumentException("Cannot add the same observer"+ " with different lifecycles");}if (existing != null) {return;}//5owner.getLifecycle().addObserver(wrapper);}

我在源码加了几个注释,我们一个个看。

注释一:可以看出通过当前组件的LifecycleOwner拿到生命周期的状态,如果是DESTORY就返回,不做任何处理。

注释二:新建了一个LifecycleBoundObserver包装类,并把owner和observer丢进去。

注释三:将observer和wrapper做一个存储。我们看下putIfAbsent方法:

public V putIfAbsent(@NonNull K key, @NonNull V v) {Entry<K, V> entry = get(key);if (entry != null) {return entry.mValue;}put(key, v);return null;}

可以看出,如何这个entry存在的话,不像正常map做替换,而且直接拿到对应的value。不存在的话正常put操作。

注释四:判断这个owner是否存在,如何存在就抛出异常,提示无法添加具有不同生命周期的同一观察者。

注释五:将LifecycleBoundObserver添加到Lifecycle中完成注册,所以我们LiveData的生命周期观察其实来源于LifecycleOwner,这也就是为什么LiveData具备了观察组件生命周期变化的能力。

observe是如何同步数据的?

上面代码中我们知道有一个LifecycleBoundObserver的包装类。我们看看其实现逻辑:

class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {@NonNullfinal LifecycleOwner mOwner;LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {super(observer);mOwner = owner;}@Overrideboolean shouldBeActive() {return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);}@Overridepublic void onStateChanged(@NonNull LifecycleOwner source,@NonNull Lifecycle.Event event) {Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();if (currentState == DESTROYED) {removeObserver(mObserver);return;}Lifecycle.State prevState = null;while (prevState != currentState) {prevState = currentState;activeStateChanged(shouldBeActive());currentState = mOwner.getLifecycle().getCurrentState();}}@Overrideboolean isAttachedTo(LifecycleOwner owner) {return mOwner == owner;}@Overridevoid detachObserver() {mOwner.getLifecycle().removeObserver(this);}}

我们来关注几个方法。通过ObserverWrapper实现了shouldBeActive。从命名上可以看出判读是否活跃。从代码看来也就是STARTED和RESUMED。

在看另一个,通过LifecycleEventObserver实现了onStateChanged方法。我们可以看到如果当前的STATE是DESTROYED时会移除观察者监听。接着通过判读状态是否匹配调用了activeStateChanged方法。因为prevState定义为null。所以此方法最少执行一次。那么我们看看它是如何处理的。

void activeStateChanged(boolean newActive) {if (newActive == mActive) {return;}// immediately set active state, so we'd never dispatch anything to inactive// ownermActive = newActive;changeActiveCounter(mActive ? 1 : -1);if (mActive) {dispatchingValue(this);}}

可以看到它会先调用changeActiveCounter,字面意思也就是改变活跃的数量。

@MainThreadvoid changeActiveCounter(int change) {int previousActiveCount = mActiveCount;mActiveCount += change;if (mChangingActiveState) {return;}mChangingActiveState = true;try {while (previousActiveCount != mActiveCount) {boolean needToCallActive = previousActiveCount == 0 && mActiveCount > 0;boolean needToCallInactive = previousActiveCount > 0 && mActiveCount == 0;previousActiveCount = mActiveCount;if (needToCallActive) {onActive();} else if (needToCallInactive) {onInactive();}}} finally {mChangingActiveState = false;}}

从逻辑上我们可以看出previousActiveCount默认为0,mActiveCount可能为1,可能为-1,mChangingActiveState默认为flase。也就是一定会走进while循环里面去。从里面代码可以看出需要通知组件存活状态就调用onActive,需要通知组件不活跃就调用onInactive。然后我们往回看,如果组件是活跃的就会调用dispatchingValue方法。我们接着看:

    void dispatchingValue(@Nullable ObserverWrapper initiator) {//1if (mDispatchingValue) {//2mDispatchInvalidated = true;return;}mDispatchingValue = true;do {//3mDispatchInvalidated = false;if (initiator != null) {considerNotify(initiator);initiator = null;} else {for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {considerNotify(iterator.next().getValue());if (mDispatchInvalidated) {break;}}}} while (mDispatchInvalidated);//4mDispatchingValue = false;}

注释一中mDispatchingValue用于标记当前是否处于分发状态中,如果是存在分发状态,则执行注释二表示分发无效并直接return。注释三则将分发状态修改成有效状态。我们可以看到后面无论initiator是否为null都会执行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;observer.mObserver.onChanged((T) mData);}

可以看到当前状态如果是非活跃的就直接return了。接着判读了如果当前observer对应组件的状态不是Active,就会再次调用activeStateChanged方法,并传入false,其方法内部会再次判断是否执行onActive方法和onInactive方法回调。接着就会调用Observer的onChange方法通知数据更新。

postValue和setValue的区别是什么?

我们知道虽然我们调用observe方法对数据进行观察,但是真正同步数据的代码是postValue和setValue。话不多说,上代码:

    private final Runnable mPostValueRunnable = new Runnable() {@SuppressWarnings("unchecked")@Overridepublic void run() {Object newValue;synchronized (mDataLock) {newValue = mPendingData;mPendingData = NOT_SET;}setValue((T) newValue);}};...protected void postValue(T value) {boolean postTask;synchronized (mDataLock) {postTask = mPendingData == NOT_SET;mPendingData = value;}if (!postTask) {return;}ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);}@MainThreadprotected void setValue(T value) {assertMainThread("setValue");mVersion++;mData = value;dispatchingValue(null);}

我们看到setValue加上了@MainThread注解,也就是在主线程中执行,并且会调用dispatchingValue传入null。这个前面我们有提到过。而postValue其实是在子线程操作,最后通过postToMainThread同步至主线程,最后其实还是调用了setValue方法。

Transformations分析

前面我们在使用篇时有提到Transformations有2个方法,一个是map。一个是switchMap。我们一个个的看。

map
 @MainThread@NonNullpublic static <X, Y> LiveData<Y> map(@NonNull LiveData<X> source,@NonNull final Function<X, Y> mapFunction) {final MediatorLiveData<Y> result = new MediatorLiveData<>();result.addSource(source, new Observer<X>() {@Overridepublic void onChanged(@Nullable X x) {result.setValue(mapFunction.apply(x));}});return result;}

我们可以看到此处调用了MediatorLiveData的addSource方法:

    @MainThreadpublic <S> void addSource(@NonNull LiveData<S> source, @NonNull Observer<? super S> onChanged) {Source<S> e = new Source<>(source, onChanged);Source<?> existing = mSources.putIfAbsent(source, e);if (existing != null && existing.mObserver != onChanged) {throw new IllegalArgumentException("This source was already added with the different observer");}if (existing != null) {return;}if (hasActiveObservers()) {e.plug();}}

可以看到最后一个判断,如果存在活跃的观察者,即调用Source的plug方法。

private static class Source<V> implements Observer<V> {final LiveData<V> mLiveData;final Observer<? super V> mObserver;int mVersion = START_VERSION;Source(LiveData<V> liveData, final Observer<? super V> observer) {mLiveData = liveData;mObserver = observer;}void plug() {mLiveData.observeForever(this);}void unplug() {mLiveData.removeObserver(this);}@Overridepublic void onChanged(@Nullable V v) {if (mVersion != mLiveData.getVersion()) {mVersion = mLiveData.getVersion();mObserver.onChanged(v);}}}

我们可以看到Source提供了添加观察者,移除观察者,以及监听的操作。哎,不对,是不是发现哪里有点问题?这里它调用的是observeForever,而不是addObserver。那我们接着看:

    public void observeForever(@NonNull Observer<? super T> observer) {assertMainThread("observeForever");AlwaysActiveObserver wrapper = new AlwaysActiveObserver(observer);ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);if (existing instanceof LiveData.LifecycleBoundObserver) {throw new IllegalArgumentException("Cannot add the same observer"+ " with different lifecycles");}if (existing != null) {return;}wrapper.activeStateChanged(true);}

可以看出,这里最后其实还是调用了ObserverWrapper的activeStateChanged方法。

switchMap
    @MainThread@NonNullpublic static <X, Y> LiveData<Y> switchMap(@NonNull LiveData<X> source,@NonNull final Function<X, LiveData<Y>> switchMapFunction) {final MediatorLiveData<Y> result = new MediatorLiveData<>();result.addSource(source, new Observer<X>() {LiveData<Y> mSource;@Overridepublic void onChanged(@Nullable X x) {LiveData<Y> newLiveData = switchMapFunction.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;}

可以看出来,整体和map类似,只是内部包装了一层,让其返回结果是一个LiveData而已。

总结

本篇我们整体介绍了LiveData的使用原理,也就是上篇文章遗留下来的几个问题。总之,我们不仅需要知其然,还需要知其所以然。

参考

Android Jetpack架构组件(五)带你了解LiveData(原理篇)

本文首发于我的个人博客:Android Jetpack架构组件——LiveData原理篇

更多文章请关注我的公众号:码农职场

在这里插入图片描述

这篇关于Android Jetpack架构组件——LiveData原理篇的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/758065

相关文章

Vue项目中Element UI组件未注册的问题原因及解决方法

《Vue项目中ElementUI组件未注册的问题原因及解决方法》在Vue项目中使用ElementUI组件库时,开发者可能会遇到一些常见问题,例如组件未正确注册导致的警告或错误,本文将详细探讨这些问题... 目录引言一、问题背景1.1 错误信息分析1.2 问题原因二、解决方法2.1 全局引入 Element

vue解决子组件样式覆盖问题scoped deep

《vue解决子组件样式覆盖问题scopeddeep》文章主要介绍了在Vue项目中处理全局样式和局部样式的方法,包括使用scoped属性和深度选择器(/deep/)来覆盖子组件的样式,作者建议所有组件... 目录前言scoped分析deep分析使用总结所有组件必须加scoped父组件覆盖子组件使用deep前言

基于Qt Qml实现时间轴组件

《基于QtQml实现时间轴组件》时间轴组件是现代用户界面中常见的元素,用于按时间顺序展示事件,本文主要为大家详细介绍了如何使用Qml实现一个简单的时间轴组件,需要的可以参考下... 目录写在前面效果图组件概述实现细节1. 组件结构2. 属性定义3. 数据模型4. 事件项的添加和排序5. 事件项的渲染如何使用

Android数据库Room的实际使用过程总结

《Android数据库Room的实际使用过程总结》这篇文章主要给大家介绍了关于Android数据库Room的实际使用过程,详细介绍了如何创建实体类、数据访问对象(DAO)和数据库抽象类,需要的朋友可以... 目录前言一、Room的基本使用1.项目配置2.创建实体类(Entity)3.创建数据访问对象(DAO

Android WebView的加载超时处理方案

《AndroidWebView的加载超时处理方案》在Android开发中,WebView是一个常用的组件,用于在应用中嵌入网页,然而,当网络状况不佳或页面加载过慢时,用户可能会遇到加载超时的问题,本... 目录引言一、WebView加载超时的原因二、加载超时处理方案1. 使用Handler和Timer进行超

JS常用组件收集

收集了一些平时遇到的前端比较优秀的组件,方便以后开发的时候查找!!! 函数工具: Lodash 页面固定: stickUp、jQuery.Pin 轮播: unslider、swiper 开关: switch 复选框: icheck 气泡: grumble 隐藏元素: Headroom

mybatis的整体架构

mybatis的整体架构分为三层: 1.基础支持层 该层包括:数据源模块、事务管理模块、缓存模块、Binding模块、反射模块、类型转换模块、日志模块、资源加载模块、解析器模块 2.核心处理层 该层包括:配置解析、参数映射、SQL解析、SQL执行、结果集映射、插件 3.接口层 该层包括:SqlSession 基础支持层 该层保护mybatis的基础模块,它们为核心处理层提供了良好的支撑。

百度/小米/滴滴/京东,中台架构比较

小米中台建设实践 01 小米的三大中台建设:业务+数据+技术 业务中台--从业务说起 在中台建设中,需要规范化的服务接口、一致整合化的数据、容器化的技术组件以及弹性的基础设施。并结合业务情况,判定是否真的需要中台。 小米参考了业界优秀的案例包括移动中台、数据中台、业务中台、技术中台等,再结合其业务发展历程及业务现状,整理了中台架构的核心方法论,一是企业如何共享服务,二是如何为业务提供便利。

如何在页面调用utility bar并传递参数至lwc组件

1.在app的utility item中添加lwc组件: 2.调用utility bar api的方式有两种: 方法一,通过lwc调用: import {LightningElement,api ,wire } from 'lwc';import { publish, MessageContext } from 'lightning/messageService';import Ca

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo