LiveData使用

前言

LiveData更新数据的方法。创建一个LiveData,调用observe方法,传入生命周期owner对象和自定义的观察者。

kotlin 复制代码
val data:LiveData<String> = MutableLiveData("test")
data.observe(this,object :Observer<String>{
    override fun onChanged(value: String) {
        println("data changed $value")
    }
})

LiveData源码中的重要属性和方法

java 复制代码
public abstract class LiveData<T> {
    // 改变数据后的标记
    private int mVersion;
    // 迭代更新数据的Iterable
    private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers =
        new SafeIterableMap<>();
        
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
    // 检测是否时主线程
    assertMainThread("observe");
    // 检测owner生命周期
    if (owner.getLifecycle().getCurrentState() == DESTROYED) {
        // ignore
        return;
    }
    // 生命周期owner和观察者observer包装为wrapper
    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
    // 观察者observer作为key把wrapper存入集合mObservers
    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;
    }
    // 包装后的wrapper给到LifecycleRegistry
    owner.getLifecycle().addObserver(wrapper);
}
}

LiveData对象有mObservers集合,存储观察者(一个LiveData对象可以有多个观察者)。

java 复制代码
@MainThread
protected void setValue(T value) {
    // 检测是否在主线程
    assertMainThread("setValue");
    // 每次更新数版本属性++
    mVersion++;
    // 更新数据
    mData = value;
    // 分发数据
    dispatchingValue(null);
}

从源码可以看到setValue需要在主线程调用。

java 复制代码
protected void postValue(T value) {
    boolean postTask;
    // 锁保护线程安全,即支持主线程调用
    synchronized (mDataLock) {
        postTask = mPendingData == NOT_SET;
        mPendingData = value;
    }
    if (!postTask) {
        return;
    }
    // 切换到主线程run一个runnable
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
private final Runnable mPostValueRunnable = new Runnable() {
    @SuppressWarnings("unchecked")
    @Override
    public void run() {
        Object newValue;
        synchronized (mDataLock) {
            newValue = mPendingData;
            mPendingData = NOT_SET;
        }
        setValue((T) newValue);
    }
};

postValue一般在子线程调用,mPostValueRunnable(主线程)里调用了setValue。所以数据的更新postValue最终还是通过setValue更新的。分发数据:

java 复制代码
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
                considerNotify(iterator.next().getValue());
                if (mDispatchInvalidated) {
                    break;
                }
            }
        }
    } while (mDispatchInvalidated);
    mDispatchingValue = false;
}

considerNotify对observer状态过滤,版本判断,通过后,对mObserverde的onChanged进行数据回调。

kotlin 复制代码
@SuppressWarnings("unchecked")
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.
    // 检测当前mOwner状态是否符合(state>=STARTED 符合状态的有:STARTED和RESUME)
    if (!observer.shouldBeActive()) {
        observer.activeStateChanged(false);
        return;
    }
    if (observer.mLastVersion >= mVersion) {
        return;
    }
    observer.mLastVersion = mVersion;
    observer.mObserver.onChanged((T) mData);
}

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

从上面看出LiveData更新数据必须在STARTED和RESUME状态,否则不分发。 mVersion是int类型,比long和double节省内存。mVersion++没有判断最大值(Integer.MAX_VALUE)判断,当达到最大值不会报溢出(integer overflow)异常,会绕到最小值int(Integer.MIN_VALUE=-2147483648),虽然不报错,由于mLastVersion>mVersion,会触发一次return。所以下次改变数据才会真的更新。

LiveData感知生命周期

LiveData本身不具备感知生命周期。由lifecycle实现的。LifecycleOwner,一般LiveData在Activity进行观察,传入this

kotlin 复制代码
val data:LiveData<String> = MutableLiveData("test")
data.observe(this,object :Observer<String>{
    override fun onChanged(value: String) {
        println("data changed $value")
    }
})

ComponentActivity实现了LifeCycleOwner,所以observe传this这样LiveData就能感知生命周期了。所以如果想自定义可以自己实现LifeCycleOwner传入到observe中。 自定义owner:

kotlin 复制代码
/**
 * 实现LifecycleOwner,体哦那个生命周期能力
 */
class CustomLifecycleOwner : LifecycleOwner, LifecycleObserver {
    /**
     *
     */
    private val mRegistry = LifecycleRegistry(this)

    init {
        // 添加生命周期观察者
        mRegistry.addObserver(this)
        // 参考Activity生命周期流程,初始化生命周期状态
        mRegistry.currentState = Lifecycle.State.INITIALIZED
    }

    override val lifecycle: Lifecycle
        get() = mRegistry

    /**
     * 维护生命周期状态
     */
    fun funCreate() {
        mRegistry.currentState = Lifecycle.State.CREATED
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    fun funOnCreateCallback() {
        println("funOnCreateCallback")
    }

    fun funStared() {
        // liveData在owner状态>=STARTED才会分发数据
        mRegistry.currentState = Lifecycle.State.STARTED
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    fun funOnStartCallback() {
        println("funOnStartCallback")
    }

    fun funOnDestroy() {
        mRegistry.currentState = Lifecycle.State.DESTROYED
        // 取消注册
        mRegistry.removeObserver(this)
    }
}

测试:

kotlin 复制代码
val data = MutableLiveData("test")
val owner = CustomLifecycleOwner()
data.observe(owner, object : Observer<String> {
    override fun onChanged(value: String) {
        println("data changed $value")
    }
})
owner.funCreate()
data.value = "111"
Handler().postDelayed({
    owner.funStared()
    data.value = "222"
}, 2000)

可以看到observer分别打印了111和222日志。onCreate状态下,set=111,由于生命周期关系没有分发,所以没有收到。后面延迟2秒,onStart后,收到了111和222数据。可以看到liveData具有粘性。

LiveData具有粘性原因

从上面可以看到funStarted触发mRegistry.currentState = Lifecycle.State.STARTED触发流程:

kotlin 复制代码
Lifecycle.State.STARTED-->setCurrentState-->moveToState-->sync-->forwardPass-->observer.dispatchEvent-->mLifecycleObserver.onStateChanged(owner,event)-->activeStateChanged(shouldBeActive())-->dispatchingValue(this)-->considerNotify(initiator)-->observer.mObserver.onChanged((T)mData)

当调用Lifecycle.State.STARTED的时候就会默认触发一次onChanged去更新observer观察者,粘性触发(发送距离Lifecycle.State.STARTED最近的一次数据更新)

liveData丢失数据的情况

如果子线程或多线程频繁调用liveData.postValue,就会丢失数据。官方不建议这样去使用LiveData(数据的改变没有缓存起来,就只是用了一个当前成员mData保存最新改变的数据)。非要用liveData处理,那就单线程每次更新加延迟,或者频繁切换线程setValue去更新,虽然可以,代价就是浪费性能。如果业务一定需要,建议用Rxjava或Flow。

Fragment使用LiveData

LiveData类中LifecycleBoundObserver,LiveData绑定的LiecycleOwner生命周期变化为onDestroy时会移除对应的观察者。所以Activity作为LifecycleOwner时可以自动处理LiveData的解注册问题。LiveData类中LifecycleBoundObserver中源码:

kotlin 复制代码
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
        @NonNull Lifecycle.Event event) {
    Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
    if (currentState == DESTROYED) {
        // onDestroy的时候移除了Observer
        removeObserver(mObserver);
        return;
    }
    Lifecycle.State prevState = null;
    while (prevState != currentState) {
        prevState = currentState;
        activeStateChanged(shouldBeActive());
        currentState = mOwner.getLifecycle().getCurrentState();
    }
}

然而,Fragment加载方式有add和replace两种

  • 使用add加载新的Fragment,原本旧的Fragment不会被移除栈,旧的Fragment就不会触发onDestroy,LiveData就不会解注册
  • 使用replace加载Fragment,旧的Fragment进行出栈,旧的触发onDestroy,LiveData可以解除注册。

Fragment用LiveData,那只能用replace不能用add吗。我们可以使用ViewLifecycleOwner,其实就是FragmentViewLifecycleOwner。官方推荐用viewLifecycleOwner,在视图不显示的时候,会触发onDestroy帮助我们进行LiveData解注册。

一直监听LiveData,无论是否活跃(STARTED)

可以使用observeForever函数,是没有传lifecycleOwner的只有观察者一个参数。没有owner就没有生命周期了。默认一直活跃,需要自己解注册处理。

kotlin 复制代码
@MainThread
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);
}

每次传入observer都会包装成一个新的wrapper,再调用每个新的wrapper的activeStateChanged(true)。一个LiveData支持多个observeForever,即使传同一个观察者,它也会包装为新的wrapper。

LiveData转换数据类型

kotlin 复制代码
// 获取用户信息
val data = MutableLiveData<UserInfo>()
val userLv:LiveData<Int> = Transformations.map(data){
    it.userLv
}
val userLv:LiveData<Int> = Transformations.switchMap(data){
    MutableLiveData(it.userLv)
}

例如:使用一个接口的回调结果展示用户信息;后面还有其他业务需要监听用户等级变化,展示其他业务。使用Transformations可以实现。 除了map还有switchMap,switchMap每次返回一个新的LiveData对象。普通类型转换,用map就可以了。使用switchMap场景:

  • 假如ViewModel暴露一个itemsLiveData给用户观察。我们通过用户的意图(提供一个带有参数的getItemsLiveData方法),用户可选择数据库的旧数据或网络请求的新数据
kotlin 复制代码
// 这是本地数据源
private val localDataSource = MutableLiveData<List<Item>>()
// 这是网络数据源
private val networkDataSource = MutableLiveData<List<Item>>()
// 暴露给用户的liveData
val itemsLiveData = MutableLiveData<List<Item>>()

// 用于获取最终数据流的方法
fun getItems(dataSourceType: DataSourceType): LiveData<List<Item>> {
    // 使用switchMap根据dataSourceType的值切换数据源
    Transformations.switchMap(dataSourceType) { type ->
        when (type) {
            DataSourceType.LOCAL -> {
                // 如果选择本地数据源,返回本地数据的LiveData
                localDataSource
            }

            DataSourceType.NETWORK -> {
                // 如果选择网络数据源,返回网络数据的LiveData
                networkDataSource
            }
        }
    }
}

switchMap每次都会创建一个新的LiveData不适合频繁调用情况,浪费性能。可以使用MediatorLiveData

kotlin 复制代码
// 这是本地数据源
private val localDataSource = MutableLiveData<List<Item>>()
// 这是网络数据源
private val networkDataSource = MutableLiveData<List<Item>>()
// 暴露给用户的liveData
val mediatorLiveData = MediatorLiveData<List<Item>>()

// 用于获取最终数据流的方法
fun getItems(dataSourceType: DataSourceType): LiveData<List<Item>> {
    // 使用switchMap根据dataSourceType的值切换数据源
        when (dataSourceType) {
            DataSourceType.LOCAL -> {
                // 如果选择本地数据源,返回本地数据的LiveData
                localDataSource.value = loadLocalData()
                mediatorLiveData.addSource(localDataSource) {
                    mediatorLiveData.value = localDataSource.value
                }
                return localDataSource
            }
            else ->{
                // 如果选择网络数据源,返回网络数据的LiveData
                networkDataSource.value = getNetWorkData()
                mediatorLiveData.addSource(localDataSource) {
                    mediatorLiveData.value = localDataSource.value
                }
                return networkDataSource
            }
        }
    }

如果不用上面的方案,直接定义一个LiveData,数据来了,无论网络还是本地、别人传的,都用setValue,这样也行,但是不符合常理(需要根据业务分析是不是多余)。

相关推荐
Estar.Lee12 分钟前
时间操作[计算时间差]免费API接口教程
android·网络·后端·网络协议·tcp/ip
找藉口是失败者的习惯1 小时前
从传统到未来:Android XML布局 与 Jetpack Compose的全面对比
android·xml
Jinkey2 小时前
FlutterBasic - GetBuilder、Obx、GetX<Controller>、GetxController 有啥区别
android·flutter·ios
大白要努力!4 小时前
Android opencv使用Core.hconcat 进行图像拼接
android·opencv
天空中的野鸟5 小时前
Android音频采集
android·音视频
小白也想学C6 小时前
Android 功耗分析(底层篇)
android·功耗
曙曙学编程6 小时前
初级数据结构——树
android·java·数据结构
闲暇部落8 小时前
‌Kotlin中的?.和!!主要区别
android·开发语言·kotlin
诸神黄昏EX10 小时前
Android 分区相关介绍
android
大白要努力!11 小时前
android 使用SQLiteOpenHelper 如何优化数据库的性能
android·数据库·oracle