LiveData基本使用与问题分析

介绍

LiveData是可观察的数据存储类,且具有生命周期感知,能实现只更新活跃的观察者(如 Activity、Fragment onResume 等活跃生命周期状态)

理解:
数据存储类,可理解为对原数据的包装,持有原数据
可观察的,LiveData可以被Observer(观察者)观察,数据更新时通知Observer(活跃状态)
活跃状态,更新时只通知活跃状态的观察者,节省资源,也无需担心因生命周期结束后继续操作相关组件导致闪退(如Activity finish 后操作界面);非活跃状态的观察者可延迟更新,即从非活跃状态变为活跃状态,会收到之前最新一次的更新通知,以达到数据同步。
具有生命周期感知,就无需手动解除观察,也不会有内存泄露问题。

使用

  1. 创建 LiveData,指定数据类型
  2. 注册观察者
  3. LiveData 更新数据,观察者接收更新通知

private var testLiveData: MutableLiveData<String> = MutableLiveData()

创建LiveData,一般使用LiveData的实现类MutableLiveData,并指定数据类型

public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer)

testLiveData.observe(this) {
    LogUtils.e("ccc0425", "observe string = $it")
}

注册观察者,observe接收LifecycleOwner 和 Observer,LifecycleOwner 生命周期感知,数据更新时回调 Observer onChange 方法;另 observeForever 方法也可注册观察者,无需LifecycleOwner参数,但无法感知生命周期,除解除观察外,皆可接收通知,有特殊需求可使用。

testLiveData.value = "set value"
testLiveData.postValue("post value")

更新数据有setValue 和 postValue 两个方法,如在子线程,必须使用postValue

数据共享

自定义LiveData,实现单例模式,可实现数据共享

class StockLiveData private constructor(symbol: String) : LiveData<String>() {
    //活跃的观察者(LifecycleOwner)数量从 0 变为 1 时调用
   override fun onActive() {
       LogUtils.e("ccc0425", "onActive")
    }

    //活跃的观察者(LifecycleOwner)数量从 1 变为 0 时调用。这不代表没有观察者了,可能是全都不活跃了。可以使用hasObservers()检查是否有观察者。
    override fun onInactive() {
        LogUtils.e("ccc0425", "onInactive")
    }
    companion object {
        private var sInstance //单实例
                : StockLiveData? = null
        //获取单例
        @MainThread
        operator fun get(symbol: String): StockLiveData? {
            if (sInstance == null) {
                sInstance = StockLiveData(symbol)
            }
            return sInstance
        }
    }
}

可以实现onActive和onInactive方法,做相关业务处理,如开启或关闭业务数据监测。单例实现可以多个Activity或Fragment获取同一LiveData对象,实现相关数据共享。

数据修改

private var testLiveData: MutableLiveData<String> = MutableLiveData()
private var testChange = Transformations.map(testLiveData) {
    return@map "$it change"
}
testChange.observe(this) {
    LogUtils.e("ccc0425", "change = $it")
}

通过Transformations.map将testLiveData的数据进行修改,将修改后的数据更新通知给观察者

数据切换

private var testLiveData: MutableLiveData<String> = MutableLiveData()
private var testLiveData2: MutableLiveData<String> = MutableLiveData()
private var switchData :MutableLiveData<Boolean> = MutableLiveData()
private var switchDataMap = Transformations.switchMap(switchData,
    Function<Boolean, LiveData<String>> {
        if (it) {
            return@Function testLiveData
        }
        return@Function testLiveData2
    })
switchDataMap.observe(this) {
    LogUtils.e("ccc0425", "switch = $it")
}

Transformations.switchMap 可根据不同条件返回不同的LiveData

多数据源

private var testMultiData = MediatorLiveData<String>()
testMultiData.addSource(testLiveData) {
    LogUtils.e("ccc0425", "liveData1 = $it")
    testMultiData.value = it
}
testMultiData.addSource(testLiveData2) {
    LogUtils.e("ccc0425", "liveData2 = $it")
    testMultiData.value = it
}
testMultiData.observe(this){
    LogUtils.e("ccc0425", "testMultiData = $it")
}

任一数据源发生改变均会通知到多数据源观察者

问题

数据倒灌

前面提到非活跃状态的观察者可延迟更新,即从非活跃状态变为活跃状态,会收到之前最新一次的更新通知,以达到数据同步。先发送了数据更新通知,后注册观察者,观察者也能收到最新的更新通知,此设计可以达到在页面重建时无需网络获取也能刷新页面数据。但是在使用数据共享时,就会出现第二次进入页面时会收到前一次的数据更新,导致页面状态出错。如 ActivityA 和 ActivityB 使用同一LiveData,A为展示页面,B为创作页面,AB均接收LiveData的通知,A中用于展示内容,B中用于处理结果,当第二次进入B时,注册了观察者之后会收到第一次的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");
        }
        //一个observer只注册一次
        if (existing != null) {
            return;
        }
        owner.getLifecycle().addObserver(wrapper);
    }

找到 Lifecycle 的实现类 LifecycleRegistry 的 addObserver 方法

    @Override
    public void addObserver(@NonNull LifecycleObserver observer) {
        enforceMainThreadIfNeeded("addObserver");
        State initialState = mState == DESTROYED ? DESTROYED : INITIALIZED;
        ObserverWithState statefulObserver = new ObserverWithState(observer, initialState);
        ObserverWithState previous = mObserverMap.putIfAbsent(observer, statefulObserver);

        if (previous != null) {
            return;
        }
        LifecycleOwner lifecycleOwner = mLifecycleOwner.get();
        if (lifecycleOwner == null) {
            // it is null we should be destroyed. Fallback quickly
            return;
        }

        boolean isReentrance = mAddingObserverCounter != 0 || mHandlingEvent;
        State targetState = calculateTargetState(observer);
        mAddingObserverCounter++;
        while ((statefulObserver.mState.compareTo(targetState) < 0
                && mObserverMap.contains(observer))) {
            pushParentState(statefulObserver.mState);
            final Event event = Event.upFrom(statefulObserver.mState);
            if (event == null) {
                throw new IllegalStateException("no event up from " + statefulObserver.mState);
            }
            statefulObserver.dispatchEvent(lifecycleOwner, event);
            popParentState();
            // mState / subling may have been changed recalculate
            targetState = calculateTargetState(observer);
        }

        if (!isReentrance) {
            // we do sync only on the top level.
            sync();
        }
        mAddingObserverCounter--;
    }

查看statefulObserver.dispatchEvent(lifecycleOwner, event);方法

    static class ObserverWithState {
        State mState;
        LifecycleEventObserver mLifecycleObserver;

        ObserverWithState(LifecycleObserver observer, State initialState) {
            mLifecycleObserver = Lifecycling.lifecycleEventObserver(observer);
            mState = initialState;
        }

        void dispatchEvent(LifecycleOwner owner, Event event) {
            State newState = event.getTargetState();
            mState = min(mState, newState);
            mLifecycleObserver.onStateChanged(owner, event);
            mState = newState;
        }
    }
    @NonNull
    static LifecycleEventObserver lifecycleEventObserver(Object object) {
        boolean isLifecycleEventObserver = object instanceof LifecycleEventObserver;
        boolean isFullLifecycleObserver = object instanceof FullLifecycleObserver;
        if (isLifecycleEventObserver && isFullLifecycleObserver) {
            return new FullLifecycleObserverAdapter((FullLifecycleObserver) object,
                    (LifecycleEventObserver) object);
        }
        if (isFullLifecycleObserver) {
            return new FullLifecycleObserverAdapter((FullLifecycleObserver) object, null);
        }

        if (isLifecycleEventObserver) {
            return (LifecycleEventObserver) object;
        }
        ........
    }

mLifecycleObserver 就是LiveData中调用addObserver传入的LifecycleBoundObserver对象

    class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
        @NonNull
        final LifecycleOwner mOwner;

        LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
            super(observer);
            mOwner = owner;
        }

        @Override
        boolean shouldBeActive() {
            return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
        }

        @Override
        public 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();
            }
        }

        @Override
        boolean isAttachedTo(LifecycleOwner owner) {
            return mOwner == owner;
        }

        @Override
        void detachObserver() {
            mOwner.getLifecycle().removeObserver(this);
        }
    }

onStateChanged方法中调用了activeStateChanged方法

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

找到dispatchingValue方法

    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<? super T>, ObserverWrapper>> iterator =
                        mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                    considerNotify(iterator.next().getValue());
                    if (mDispatchInvalidated) {
                        break;
                    }
                }
            }
        } while (mDispatchInvalidated);
        mDispatchingValue = false;
    }

initiator为空时,循环整个观察者Map,这里调用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);
    }

当observer 的 mLastVersion 小于 LiveData 的 mVersion 时,回调onChange的方法
mLastVersion的初始值是-1,mVerSion的初始值为-1或1(无参构造函数-1、带参1)且在调用setValue时+1(postValue最终也会调用setValue)。之前场景中,LiveData第一次已经调用了setValue,mVersion是1,第二次进入B时observer.mLastVersion是初始值-1,这就解释了后注册的观察者也会收到之前最新的一次更新通知。

处理

mVersion是LiveData中的私有变量可利用反射获取具体数值进行判断
创建LiveData包装类,处理是否接收注册前的更新事件

    class BaseLiveData<T>:  MutableLiveData<T> {

    constructor(): super()
    constructor(t: T): super(t)

    fun observe(owner: LifecycleOwner, isSticky: Boolean, observer: Observer<in T>) {
        if (isSticky) {
            super.observe(owner, observer)
        } else {
            val mVersion = getLiveDataMVersion(this)
            super.observe(owner, StickyObserver<T>(observer, mVersion, this))
        }
    }

    fun observeForever(isSticky: Boolean, observer: Observer<in T>) {
        if (isSticky) {
            super.observeForever(observer)
        } else {
            val mVersion = getLiveDataMVersion(this)
            super.observeForever(StickyObserver<T>(observer, mVersion, this))
        }
    }

    class StickyObserver<T>(private val observer: Observer<in T>, mVersion: Int, private val liveData: LiveData<T>): Observer<T> {
        var mLastVersion :Int = mVersion

        override fun onChanged(t: T) {
            val newVersion = getLiveDataMVersion(liveData)
            if (mLastVersion < newVersion) {
                observer.onChanged(t)
            }
            mLastVersion = newVersion
        }

    }


}

fun <T> getLiveDataMVersion(liveData: LiveData<T>): Int {
    val clazz = LiveData::class.java
    val field = clazz.getDeclaredField("mVersion")
    field.isAccessible = true
    val mVersion = field.getInt(liveData)
    return mVersion
}

BaseLiveData 继承 MutableLiveData,实现 observe 和 observeForever 方法(不是重写 需要新增参数)
以 observe 为例,如果 isSticky 为 true ,不做修改,若为 false ,StickyObserver 作为 super.observe 的参数,StickyObserver 继承自 Observer ,mLastVersion 初始值设置为 LiveData 中 mVersion 的值,实现 onChanged ,方法中判断 mLastVersion 和当前 mVersion 的大小从而判断是否回调观察者的 onChanged 方法,并更新 mLastVersion 的值

Demo测试
    testLiveData()
    testStickyLiveData.observe(this, false) {
        LogUtils.e("ccc0602", "test it = $it")
    }

    Handler().postDelayed({
        testLiveData()
    }, 5000)
2021-06-02 15:59:50.118 10889-10889/com.cc.kotlin_base_mvvm E/ccc0602: testStickyLiveData set data
2021-06-02 15:59:50.118 10889-10889/com.cc.kotlin_base_mvvm E/ccc0602: do observe
2021-06-02 15:59:55.120 10889-10889/com.cc.kotlin_base_mvvm E/ccc0602: testStickyLiveData set data
2021-06-02 15:59:55.121 10889-10889/com.cc.kotlin_base_mvvm E/ccc0602: test it = sticky

观察者注册前后分别更新了数据,观察者只接收到了注册之后的更新
将 isSticky 改为 true,结果如下

2021-06-02 16:00:26.687 11149-11149/com.cc.kotlin_base_mvvm E/ccc0602: testStickyLiveData set data
2021-06-02 16:00:26.687 11149-11149/com.cc.kotlin_base_mvvm E/ccc0602: do observe
2021-06-02 16:00:26.712 11149-11149/com.cc.kotlin_base_mvvm E/ccc0602: test it = sticky
2021-06-02 16:00:31.689 11149-11149/com.cc.kotlin_base_mvvm E/ccc0602: testStickyLiveData set data
2021-06-02 16:00:31.690 11149-11149/com.cc.kotlin_base_mvvm E/ccc0602: test it = sticky

与Kotlin协程使用

官方文档示例:
使用 LiveData 时,您可能需要异步计算值。例如,您可能需要检索用户的偏好设置并将其传送给界面。在这些情况下,您可以使用 liveData 构建器函数调用 suspend 函数,并将结果作为 LiveData 对象传送。
在以下示例中,loadUser() 是在其他位置声明的暂停函数。使用 liveData 构建器函数异步调用 loadUser(),然后使用 emit() 发出结果:

val user: LiveData<User> = liveData {
    val data = database.loadUser() // loadUser is a suspend function.
    emit(data)
}

liveData 构建块用作协程和 LiveData 之间的结构化并发基元。当 LiveData 变为活动状态时,代码块开始执行;当 LiveData 变为非活动状态时,代码块会在可配置的超时过后自动取消。如果代码块在完成前取消,则会在 LiveData 再次变为活动状态后重启;如果在上次运行中成功完成,则不会重启。请注意,代码块只有在自动取消的情况下才会重启。如果代码块由于任何其他原因(例如,抛出 CancellationException)而取消,则不会重启。

简单封装

fun <T> BaseViewModel.requestEmit(block: EmitBlock<T>, tag: String? = "", isShowDialog: Boolean = false, loadingMessage: String = "请求网络中..."): LiveData<T> = liveData {
    try {
        if (isShowDialog) mStateLiveData.value = ResultState.onAppLoading(loadingMessage)
        block().apply {
            when {
                isSuccess() -> {
                    mStateLiveData.value = ResultState.onAppSuccess(getResponseData() as Any)
                    emit(getResponseData())
                }
                else -> {
                    //业务返回 失败
                    val ex = AppException(getResponseCode(), getResponseMsg(), appData = if (getResponseData() == null) "" else getResponseData().toString(), tag = tag)
                    if (getResponseCode() == Constants.TOKEN_ERROR_CODE) {
                        EventHelper.getEventViewModel().tokenErrorEvent.value = Constants.TOKEN_ERROR_CODE
                        mStateLiveData.value = ResultState.onAppTokenError(ex)
                    } else {
                        ToastUtils.showShort(ex.errorMsg)
                        mStateLiveData.value = ResultState.onAppError(ex)
                    }
                }
            }
        }
    } catch (e: Throwable) {
        paresEmitException(e)
    }
}

/**
 * 异常转换异常处理
 */
fun BaseViewModel.paresEmitException(e: Throwable) {
    val ex = ExceptionHandle.handleException(e)
    if (ex.errCode == Constants.TOKEN_ERROR_CODE) {
        EventHelper.getEventViewModel().tokenErrorEvent.value = Constants.TOKEN_ERROR_CODE
        mStateLiveData.value = ResultState.onAppTokenError(ex)
    } else {
        ToastUtils.showShort(ex.errorMsg)
        mStateLiveData.value = ResultState.onAppError(ex)
    }
}

使用官方推荐的 Kotlin 协程与 LiveData 的封装
mStateLiveData 的观察者error中如需判断特定请求的特定code做特殊处理,请求时需要 tag 参数用于区分请求,success 并不做业务逻辑的处理,可做 loading处理
success 业务逻辑处理 在 requestEmit 方法返回的 LiveData 的观察者中,这个观察者只有成功时才会收到更新通知
个人觉得还是使用 BaseViewModel.request 的封装,每个请求都单独处理
PS: BaseViewModel.request 之前的 kotlin 中有提及(以前的封装方式,传入 LiveData,并观察这个 LiveData 的更新)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值