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,这样也行,但是不符合常理(需要根据业务分析是不是多余)。

相关推荐
li_liuliu33 分钟前
Android4.4 在系统中添加自己的System Service
android
C4rpeDime3 小时前
自建MD5解密平台-续
android
鲤籽鲲4 小时前
C# Random 随机数 全面解析
android·java·c#
m0_548514778 小时前
2024.12.10——攻防世界Web_php_include
android·前端·php
凤邪摩羯8 小时前
Android-性能优化-03-启动优化-启动耗时
android
凤邪摩羯8 小时前
Android-性能优化-02-内存优化-LeakCanary原理解析
android
喀什酱豆腐9 小时前
Handle
android
m0_7482329211 小时前
Android Https和WebView
android·网络协议·https
m0_7482517211 小时前
Android webview 打开本地H5项目(Cocos游戏以及Unity游戏)
android·游戏·unity
m0_7482546613 小时前
go官方日志库带色彩格式化
android·开发语言·golang