文章

LiveData

LiveData

LiveData

LiveData 基础

什么是 LiveData?

LiveData 是 Android Jetpack Lifecycle 组件中的内容。属于官方库的一部分,Kotlin/Java 均可使用。

一句话概括 LiveData:LiveData 是可感知生命周期的,可观察的,数据持有者

它的能力和作用很简单:更新 UI

LiveData 的特性

观察者的回调永远发生在主线程(observe 在主线程)

LiveData 被用来更新 UI,因此 Observer 的 onChanged() 方法在主线程回调。

背后的原理也很简单,LiveData 的 setValue() 发生在主线程(非主线程调用会抛异常,postValue() 内部会切换到主线程调用 setValue())。之后遍历所有观察者的 onChanged() 方法。

仅持有单个且最新的数据

LiveData 每次持有一个数据,并且新数据会覆盖上一个。(这个设计很好理解,数据决定了 UI 的展示,绘制 UI 时肯定要使用最新的数据,「过时的数据」应该被忽略。)

配合 Lifecycle,观察者只会在活跃状态下(STARTED 到 RESUMED)接收到 LiveData 持有的最新的数据。在非活跃状态下绘制 UI 没有意义,是一种资源的浪费。

自动取消订阅

这是 LiveData 可感知生命周期的重要表现,自动取消订阅意味着开发者无需手动写那些取消订阅的模板代码,降低了内存泄漏的可能性。

背后原理是在生命周期处于 DESTROYED 时,移除观察者。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// LifecycleBoundObserver
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();
    }
}

提供「可读可写」和「仅可读」两个版本

LiveData 提供了 mutable(MutableLiveData)和 immutable(LiveData)两个类,前者「可读可写」,后者「仅可读」。通过权限的细化,让使用者各取所需,避免由于权限泛滥导致的数据异常。

配合 DataBinding 实现「双向绑定」

LiveData 配合 DataBinding 可以实现 更新数据自动驱动 UI 变化,如果使用「双向绑定」还能实现 UI 变化影响数据的变化。

LiveData “ 缺点 “

这些也不能算是 LiveData「设计缺陷」或「LiveData 的缺点」。作为开发者应了解这些特性并在使用过程中正确处理它们。

observe 在主线程
Value 是 nullable 的
1
2
3
4
5
6
7
8
9
// LiveData
@Nullable
public T getValue() {
    Object data = mData;
    if (data != NOT_SET) {
        return (T) data;
    }
    return null;
}

LiveData#getValue() 是可空的,使用时应该注意判空。

使用正确的 lifecycleOwner

androidx fragment 1.2.0 起,添加了新的 Lint 检查,以确保您在从 onCreateView()、onViewCreated() 或 onActivityCreated() 观察 LiveData 时使用 getViewLifecycleOwner()
1j45f
fragment 拥有两个生命周期:fragment 自身和 fragment 内部 view 的生命周期

当需要观察 view 相关的 LiveData ,可以在 onCreateView()、onViewCreated() 或 onActivityCreated()  中 LiveData observe 方法中传入 viewLifecycleOwner 而不是传入 this

黏性事件

具体见下面的解决方案。

默认不防抖

SetValue()/postValue() 传入相同的值多次调用,观察者的 onChanged() 会被多次调用。

官方在 Transformations 中提供了 distinctUntilChanged() 方法,配合官方提供的扩展函数,如下使用即可:

1
2
3
4
5
6
7
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    
    viewModel.headerText.distinctUntilChanged().observe(viewLifecycleOwner) {
        header.text = it
    }
}
Transformation 工作在主线程

有些时候我们从 repository 层拿到的数据需要进行处理,例如从数据库获得 User List,我们想根据 id 获取某个 User。

此时我们可以借助 MediatorLiveData 和 Transformatoins 来实现:

1
2
3
4
5
class MainViewModel {
  val viewModelResult = Transformations.map(repository.getDataForUser()) { data ->
     convertDataToMainUIModel(data)
  }
}

Map 和 switchMap 内部均是使用 MediatorLiveData#addSource() 方法实现的,而该方法会在主线程调用,使用不当会有性能问题。

MutableLiveData

MutableLiveData 是 LiveData 的子类,添加了公共方法 setValue 和 postValue,方便开发者直接使用。setValue 必须在主线程调用。postValue 可以在后台线程中调用

AlwaysActiveObserver

默认情况下,LiveData 会跟 LicycleOwner 绑定,只在 active 状态下更新,如若想要不管在什么状态下都能接收到数据的更改通知的话,怎么办?这时候需要使用 AlwaysActiveObserver ,改调用 observe 方法为调用 LiveData.observeForever(Observer) 方法即可。

1
2
3
4
5
val alwaysLiveString = MutableLiveData<String>()
alwaysLiveString.observeForever {
    tv_always_live_data_result.text = "always接收倒计时:$it"
    LogUtils.i("always接收倒计时 $it 秒 ${Thread.currentThread().name}")
}

postValue 与 setValue

postValue 与 setValue 区别

  1. setValue 只能在主线程调用,同步更新数据
  2. postValue 可在后台线程调用,其内部会切换到主线程调用 setValue

多次 postValue,只有最后一次数据才更新

postValue 使用不当,可能发生接收到数据变更的通知:

1
If you called this method multiple times before a main thread executed a posted task, only the last value would be dispatched.

当连续调用 postValue 时,有可能只会收到最后一次数据更新通知。

源码:

1
2
3
4
5
6
7
8
9
10
11
protected void postValue(T value) {
    boolean postTask;
    synchronized (mDataLock) {
        postTask = mPendingData == NOT_SET; // mPendingData消费了这个才为true
        mPendingData = value; 
    }
    if (!postTask) { // 多次postValue,mPendingData为消费,这里会return掉,多次postValue无效,只是更新mPendingData的值
        return;
    }
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}

mPendingData 被成功赋值 value 后,post 了一个 Runnable;mPostValueRunnable 的实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
private final Runnable mPostValueRunnable = new Runnable() {
    @SuppressWarnings("unchecked")
    @Override
    public void run() {
        Object newValue;
        synchronized (mDataLock) { // 加锁,多线程访问
            newValue = mPendingData; // 消费掉mPendingData
            mPendingData = NOT_SET; // mPendingData消费后置空
        }
        setValue((T) newValue);
    }
};
  1. postValue 将数据存入 mPendingData,mPostValueRunnable 在 UI 线程消费 mPendingData。
  2. 在 Runnable 中 mPendingData 值还没有被消费之前,即使连续 postValue , 也不会 post 新的 Runnable
  3. mPendingData 的生产 (赋值) 和消费(赋 NOT_SET) 需要加锁

LiveData 局部刷新

支持某个属性的更新。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// T不能被混淆
fun <T, A> LiveData<T>.observeState(
    lifecycleOwner: LifecycleOwner,
    prop1: KProperty1<T, A>,
    action: (A) -> Unit
) {
    map { StateTuple1(prop1.get(it)) }
        .distinctUntilChanged()
        .observe(lifecycleOwner, Observer { (a) -> action.invoke(a) })
}

fun <T, A, B> LiveData<T>.observeState(
    lifecycleOwner: LifecycleOwner,
    prop1: KProperty1<T, A>,
    prop2: KProperty1<T, B>,
    action: (A, B) -> Unit
) {
    map { StateTuple2(prop1.get(it), prop2.get(it)) }
        .distinctUntilChanged().observe(
            lifecycleOwner,
            Observer { (a, b) ->
                action.invoke(a, b)
            }
        )
}

fun <T, A, B, C> LiveData<T>.observeState(
    lifecycleOwner: LifecycleOwner,
    prop1: KProperty1<T, A>,
    prop2: KProperty1<T, B>,
    prop3: KProperty1<T, C>,
    action: (A, B, C) -> Unit
) {
    this.map {
        StateTuple3(prop1.get(it), prop2.get(it), prop3.get(it))
    }.distinctUntilChanged().observe(lifecycleOwner) { (a, b, c) ->
        action.invoke(a, b, c)
    }
}

internal data class StateTuple1<A>(val a: A)
internal data class StateTuple2<A, B>(val a: A, val b: B)
internal data class StateTuple3<A, B, C>(val a: A, val b: B, val c: C)

// endregion

T 要 keep 住

使用:

1
2
3
4
5
viewModel.viewStates.run {
    observeState(this@MainScreenActivity, MainViewState::newsList) {
        newsRvAdapter.submitList(it)
    }
}

MediatorLiveData

MediatorLiveData 中介者 LiveData

  1. 它可以监听另一个 LiveData 的数据变化
  2. 同时也可以做为一个 liveData,被其他 Observer 观察。
  3. addSource 是 MutableLiveData 作为观察者添加观察者来观察其他 LiveData 的数据,observe 是 MutableLiveData 作为被观察者来观察多个 LiveData 的数据更新,MutableLiveData 只是作为一个中间代理人而已

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class MediaLiveDataDemo : AppCompatActivity() {

    private val liveData1: MutableLiveData<String> = MutableLiveData()
    private val liveData2: MutableLiveData<String> = MutableLiveData()
    private val mediatorLiveData: MediatorLiveData<String> = MediatorLiveData()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_media_live_data_demo)

        // 这个Observer是观察LiveData1的数据变化,此时MediaLiveData是作为观察者
        mediatorLiveData.addSource(liveData1) {
            tv_live_data_result.append("LiveData1:$it\n")
            LogUtils.i("LiveData1接收 $it ${Thread.currentThread().name}")
            mediatorLiveData.setValue(it)
        }
        mediatorLiveData.addSource(liveData2) {
            tv_live_data_result.append("LiveData2:$it\n")
            LogUtils.i("LiveData2接收 $it ${Thread.currentThread().name}")
            mediatorLiveData.value = it
            if (it == "out") {
                mediatorLiveData.removeSource(liveData2)
            }
        }

        // MediaLiveData作为被观察者,被观察,只有livedata1和livedata2更新就会收到数据更新
        mediatorLiveData.observe(this, Observer<String> {
            LogUtils.i("mediatorLiveData接收 $it ${Thread.currentThread().name}")
        })

        btn_medialivedata_livedata1.setOnClickListener {
            LogUtils.i("LiveData1发送 $it ${Thread.currentThread().name}")
            liveData1.value = "LiveData1 data=${DateUtils.formatDateToString(System.currentTimeMillis())}"
        }
        btn_medialivedata_livedata2.setOnClickListener {
            LogUtils.i("LiveData2发送 $it ${Thread.currentThread().name}")
            liveData2.value = "LiveData2 data=${DateUtils.formatDateToString(System.currentTimeMillis())}"
        }
    }
    override fun onDestroy() {
        super.onDestroy()
        mediatorLiveData.removeObservers(this)
    }
}

conbine

联合多个 livedata 数据,类似 RxJava2 的 combineLatest

1
2
3
4
5
6
7
8
9
10
11
12
13
fun <T1, T2> conbineLatest(f1: LiveData<T1>, f2: LiveData<T2>): LiveData<Pair<T1?, T2?>> = MediatorLiveData<Pair<T1?, T2?>>().also { mediator ->
    mediator.value = Pair(f1.value, f2.value)

    mediator.addSource(f1) { t1: T1? ->
        val (_, t2) = mediator.value!!
        mediator.value = Pair(t1, t2)
    }

    mediator.addSource(f2) { t2: T2? ->
        val (t1, _) = mediator.value!!
        mediator.value = Pair(t1, t2)
    }
}

更多参数 conbine 参考:
https://github.com/Zhuinden/livedata-combinetuple-kt

MediaLiveData 原理

首先看 MediaLiveData.addSource

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 参数1:要观察的LiveData   参数2:观察source的Observer
public <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()) { // 存在active的Observer,调用source.plug()
        e.plug();
    }
}

下面看 Source

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
private static class Source<V> implements Observer<V> { // 作为mLiveData的观察者
    final LiveData<V> mLiveData; // 被观察的LiveData
    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的数据变化
        mLiveData.observeForever(this);
    }

    void unplug() { // 移除观察
        mLiveData.removeObserver(this);
    }

    @Override
    public void onChanged(@Nullable V v) { // 当mLiveData数据变化时,且版本是最新的,通知MediaLiveData
        if (mVersion != mLiveData.getVersion()) {
            mVersion = mLiveData.getVersion();
            mObserver.onChanged(v);
        }
    }
}

Transformations

Transformations 允许我们把一个 LiveData 进行处理,变化成另外一个 LiveData,目前支持 mapswitchMap 两个方法,跟 RxJava 的操作类似。

在使用 LiveData 时,有时候我们想改变 LiveData 的值在 LiveData 通知其 Observers 之前,或者在有些时候我们的数据源 Repository 会返回不同的 LiveData 实例,我们需要一种数据转换的功能帮助我们去转换不同数据层的数据,而 Transformations 就是这样一个工具。

map

同 RxJava 中的 map,将源 LiveData 的数据变换后输出

map 方法原型

1
public static <X, Y> LiveData<Y> map(@NonNull LiveData<X> source,@NonNull final Function<X, Y> mapFunction)

参数 1:源 LiveData
参数 2:变换 Function

map 原理

从 Transformations.map 看:

1
2
3
4
5
6
7
8
9
10
11
12
public 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>() {
        @Override
        public void onChanged(@Nullable X x) {
            result.setValue(mapFunction.apply(x));
        }
    });
    return result;
}

通过中间代理人 MediatorLiveData 来监听源 LiveData 的数据更新,在 Observer 中应用变换后返回该 MediatorLiveData

案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class LiveDataTransformationsDemo : AppCompatActivity() {
    private val liveData = MutableLiveData<Long>()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_live_data_transformations_demo)

        Transformations.map(liveData) { input -> "map转换后$input" }
                .observe(this, Observer {
                    tv_result.append("$it\n")
                })
        timerInc()
    }
    private fun timerInc() {
        lifecycleScope.launch {
            RxUtils.countdown(30).subscribe {
                liveData.postValue(it)
            }
        }
    }
}

switchMap

同 RxJava 中的 switchMap。将一个 LiveData 转换成另外一个 LiveData,并可以手动控制这个 LiveData

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public 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;

        @Override
        public 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>() {
                    @Override
                    public void onChanged(@Nullable Y y) {
                        result.setValue(y);
                    }
                });
            }
        }
    });
    return result;
}

将 source 的变换,交给 switchMapFunction 返回的 LiveData,并交由这个 LiveData 控制数据的更新,可以自由变换后 postValue/seValue,也可以不更新这个 value 了。

案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class LiveDataTransformationsDemo : AppCompatActivity() {

    private val liveData = MutableLiveData<Long>()
    private lateinit var liveDataString: SwitchMapDemoLiveData

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_live_data_transformations_demo)
        Transformations
                .switchMap(liveData) { input ->
                    liveDataString = SwitchMapDemoLiveData(input)
                    liveDataString
                }
                .observe(this, Observer {
                    tv_result_switch_map.append("$it\n")
                })
        timerInc()
        btn_switch_map.setOnClickListener {
            liveDataString.update()
        }
    }
    private fun timerInc() {
        lifecycleScope.launch {
            RxUtils.countdown(10).subscribe {
                liveData.postValue(it)
            }
        }
    }
    inner class SwitchMapDemoLiveData(val input: Long, value: String? = "") : MutableLiveData<String>(value) {
        fun update() {
            value = "switchMap 后=$input"
        }
    }
}

distinctUntilChanged

过滤掉重复的数据,根据 equals() 方法判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static <X> LiveData<X> distinctUntilChanged(@NonNull LiveData<X> source) {
    final MediatorLiveData<X> outputLiveData = new MediatorLiveData<>();
    outputLiveData.addSource(source, new Observer<X>() {

        boolean mFirstTime = true;

        @Override
        public void onChanged(X currentValue) {
            final X previousValue = outputLiveData.getValue();
            if (mFirstTime
                    || (previousValue == null && currentValue != null)
                    || (previousValue != null && !previousValue.equals(currentValue))) {
                mFirstTime = false;
                outputLiveData.setValue(currentValue);
            }
        }
    });
    return outputLiveData;
}

案例:

1
2
3
Transformations.distinctUntilChanged(liveData)
    .observe(this, Observer {
        tv_result_map.append("$it\n")

LiveDataReactiveStreams

文档:https://developer.android.com/reference/android/arch/lifecycle/LiveDataReactiveStreams

RxJava2 转为 LiveData

透過 fromPublisher 我們可以在 ViewModel 中將 Flowable<T> 轉換成 LiveData<T>

1
implementation "android.arch.lifecycle:reactivestreams:1.0.0"

示例:

1
2
3
4
5
6
7
public final class InewsListViewModel extends AndroidViewModel {
    private LiveData<List<ArticleModel>> mListLiveData;
    public LiveData<List<ArticleModel>> loadListDatasFromFlowable() {
        Flowable<List<ArticleModel>> listFlowable = mInewsRepository.loadListDatasFlowable();
        mListLiveData = LiveDataReactiveStreams.fromPublisher(listFlowable);
        return mListLiveData;
}

RxJava+MutableLiveData

不使用 LiveDataReactiveStreams 轉換的話,我們可以在 ViewModel 中 subscribe 並用 setValue 更新 MutableLiveData,這樣就不限於 Flowable,可以使用任意的 Observable。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class UserRepository {
    // ...
    public Single<List<User>> getUsers() {
        return service.getUsers();
    }
}

public class UserViewModel extends ViewModel {
    private final MutableLiveData<List<User>> users;
    //..
    public void loadUsers() {
        disposables.add(repository.getUsers()
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribeWith(new DisposableSingleObserver<List<User>>() {
            @Override
            public void onSuccess(List<User> data) {
                users.setValue(data);
            }
            @Override
            public void onError(Throwable e) {
                // Error handle.
            }
        }));
    }
    LiveData<List<User>> getUsers() {
        return users;
    }
    //...
    @Override
    public void onCleard() {
        super.onCleard();
        disposables.clear();
    }
}

LiveData coroutine builder (ktx 中 liveData{ })

livedata{}介绍及原理

1
2
3
4
5
public fun <T> liveData(
    context: CoroutineContext = EmptyCoroutineContext,
    timeoutInMs: Long = DEFAULT_TIMEOUT,
    @BuilderInference block: suspend LiveDataScope<T>.() -> Unit
)
  1. 连接协程和 LiveData 的工具方法
  2. 可以在子线程中转换 LiveData 数据,默认在主线程
  3. timeoutInMs 可指定处于 inActive 时,block 是否取消
    1. 在后台 (inActive) 的时间<timeoutInMs,那么 block 会执行,不会被取消,不会更新 UI,等 active 时再更新 UI
    2. 在后台 (inActive) 的时间>=timeoutInMs,block 会被取消掉,等 active 时重新执行 block(如果有 delay,那么 delay 重新开始计算),等 block 执行完毕,更新 UI

原理:
用 MediatorLiveData 包装了下,默认的 CoroutineContext 为 SupervisorJob+Dispatchers.Main.immediate,默认在主线程中。

Connect Kotlin Coroutine to LiveData (连接协程和 LiveData)

7sl7o
示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fun getGameGoodsList(): LiveData<List<KittyGameGoodsItem>> =
    liveData(viewModelScope.coroutineContext) {
        val data = getGameGoodsListSuspend()
        LogUtils.logi("hacket", "getGameGoodsList()", "emit data=$data")
        emit(data)
    }
private suspend fun getGameGoodsListSuspend(): List<KittyGameGoodsItem> {
    val list = mutableListOf<KittyGameGoodsItem>()
    list.add(KittyGameGoodsItem("apple"))
    list.add(KittyGameGoodsItem("orange"))
    list.add(KittyGameGoodsItem("kitty"))
    LogUtils.logd("hacket", "getGameGoodsListSuspend()", "模拟网络请求,delay5秒后返回数据")
    delay(5000L) // 模拟网络请求
    LogUtils.logd("hacket", "getGameGoodsListSuspend()", "模拟网络请求数据返回:$list")
    return list
}

Connect Kotlin Flow (or StateFlow) to LiveData

gd8o2

1
2
3
4
val someTypeLiveData: LiveData<SomeType> =   
    stateFlow.asLiveData(
        viewModelScope.coroutineContext + Dispatchers.IO
    )

一旦 LiveData 连接到任何观察者,它就会在 stateFlow 上待命,发出它的数据。在内部对于 asLiveData 实际上也是一个 LiveData {…}

1
2
3
4
5
6
7
8
public fun <T> Flow<T>.asLiveData(
    context: CoroutineContext = EmptyCoroutineContext,
    timeoutInMs: Long = DEFAULT_TIMEOUT
): LiveData<T> = liveData(context, timeoutInMs) {
    collect {
        emit(it)
    }
}

Transformation on Background (在后台线程运行转换数据)

1
2
3
4
5
6
fun getGameGoodsListOnIO(): LiveData<List<KittyGameGoodsItem>> =
    liveData(viewModelScope.coroutineContext + Dispatchers.IO) {
        val data = getGameGoodsListSuspend()
        LogUtils.logi("hacket", "getGameGoodsList()", "emit data=$data") // 在IO
        emit(data)
    }

Connecting Multiple LiveData Source Emission (连接多个 livedata 的更新) – 直接用 MediaLiveData 也可以

w2l5n

1
2
3
4
5
6
7
liveData(viewModelScope.coroutineContext + Dispatchers.IO) {
    emitSource(repository.liveDataSourceA)
    delay(2000)
    emitSource(repository.liveDataSourceB)
    delay(2000)
    emitSource(repository.liveDataSourceC)
}

Ref

ComputableLiveData

LiveDataBus

LiveData 应用场景

LiveData 坑

postValue 数据丢失的问题

postValue 只是把传进来的数据先存到 mPendingData,然后往主线程抛一个 Runnable,在这个 Runnable 里面再调用 setValue 来把存起来的值真正设置上去,并回调观察者们。而如果在这个 Runnable 执行前多次 postValue,其实只是改变暂存的值 mPendingData,并不会再次抛另一个 Runnable。这就会出现后设置的值把前面的值覆盖掉的问题,会导致事件丢失。

1
2
3
4
5
6
7
8
9
10
11
12
13
protected void postValue(T value) {
    boolean postTask;
    synchronized (mDataLock) {
        postTask = mPendingData == NOT_SET;
        // 这里先把数据暂存起来,后来的数据会覆盖前面的
        mPendingData = value;
    }
    // 这里保证只抛一个 mPostValueRunnable
    if (!postTask) {
        return;
    }
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}

LiveData 为什么最多收到 2 个通知?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class JavaTestLiveDataActivity extends AppCompatActivity {
    
    private TestViewModel model;
 
    private String test="12345";
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_java_test_live_data);
        model = new ViewModelProvider(this).get(TestViewModel.class);
        test3();       
        model.getCurrentName().setValue("3");
    }
    private void test3() {
 
        for (int i = 0; i < 10; i++) {
            model.getCurrentName().observe(this, new Observer<String>() {
                @Override
                public void onChanged(String s) {
                    Log.v("ttt", "s:" + s);
                }
            });
        }
    }
}

实际上对于 Log 系统来说,如果他判定时间戳一致的情况下,后面的 Log 内容也一致,那么他就不会重复打印内容了。这里一定要注意这个细节,否则在很多时候,会影响我们对问题的判断。再回到我们之前没有添加 hashCode 的代码,再仔细看看也就明白了:只是 Log 打印了两条而已,但是通知是收到了 10 次的,为啥打印两条?因为你的时间戳一致,后续的内容也一致。

Observer 用 lambda 写,只有一次 log

ievvj

对于 for 循环中间使用 lambda 的场景,当你的 lambda 中没有使用外部的变量或者函数的时候,那么不管是 Java8 的编译器还是 Kotlin 的编译器都会默认帮你优化成使用同一个 lambda。

编译器的出发点是好的,for 循环中 new 不同的对象,当然会导致一定程度的性能下降 (毕竟 new 出来的东西最后都是要 gc 的),但这种优化往往可能不符合我们的预期,甚至有可能在某种场景下造成我们的误判,所以使用的时候一定要小心。

LiveData 黏性和数据倒灌(LiveData 为何会收到 Observe 之前的消息?)

https://caoyangim.github.io/2020/12/19/LiveData后续/

黏性事件 (Sticky Event)

粘性事件:事件发送后,观察者才订阅,订阅后会收到之前的事件(LiveData 所维护的数据具有粘性)。

具体代码中指的是,先 setValue/postValue,后调用 observe(),如果成功收到了回调,即为粘性事件

比如:一个订阅者订阅了 LiveData,该 LiveData 调用 setValue 发送一个事件弹出一次 toast。

正常情况下是先订阅了,然后再发送数据;如果正常情况下是对的,但当屏幕旋转了或其他情况导致 Activity 重建了,再次订阅了该 LiveData,那么又会弹出一次 toast,显然这不是我们想要的。

LiveData 的设计也不是为了传递一次性事件的,它是为了反应 View 当前状态的,View 层只需要根据当前 LiveData 的值去渲染数据就行,非激活状态时 View 都不可见,就算更新了也没意义,只关心最新的值。

数据倒灌

一个 LiveData 被多次订阅后,每次都会收到最新的值;存在数据倒灌,一定是黏性的。

先 setValue/postValue,后调用 observe(new Obs()),至此收到了回调。然后再第二次调用 observe(new anotherObs()),如果还能收到第一次的回调,则为 “ 数据倒灌 “。

只要将 LiveData 变为 “ 非粘性 “ 的,就一定不会出现数据倒灌的问题了。

数据倒灌原因
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// LiveData Android29
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) { // 1
        return;
    }
    observer.mLastVersion = mVersion;
    observer.mObserver.onChanged((T) mData);
}

ObserverWrapper.mLastVersion=-1,在 observe 时,observer.mLastVersion >= mVersion 不成立,会立即将 LiveData 最后的数据倒灌给 Observer。

解决方案

SingleLiveEvent(不能解决黏性问题,解决的是先 setValue 后 observe 的数据倒灌问题,多个 observe 只有第一个能接收值)

SingleLiveEvent.java
https://juejin.im/post/5b2b1b2cf265da5952314b63

  • SingleLiveEvent 特性
1
2
3
4
1. SingleLiveEvent,顾名思义,是一个只会发送一次更新的 LiveData
2. SingleLiveEvent只适合单个Observer,只有第一个Observer会接收到更新,有多个Observer订阅时就无效了
3. SingleLiveEvent解决的并不是粘性事件的问题,而是“数据倒灌”的问题;先setValue再observe还是能收到旧值
4. SingleLiveEvent适用场景:一次setValue后,多次observe,却只想消费一个observe。但是,SingleLiveEvent的问题在于它仅限于一个观察者。如果您无意中添加了多个,则只会调用一个,并且不能保证哪一个。
  • SingleLiveEvent 原理
    SingleLiveEvent 维护了一个 mPending,默认值为 false 的 AtomicBoolean;在每次 onChanged 触发时,通过 mPending.compareAndSet(true, false) 来判断,期望为 false,更改为 true,默认 mPending 默认为 false,调用 setValue 后 mPending 更新为 true;也就是说,调用一次 setValue,mPending=true,此时第一个 Observer 会收到更新,并将 mPending 改为 true,那么第二个及后面的订阅者就收不到数据的更新了
  • SingleLiveEvent 源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class SingleLiveEvent<T> extends MutableLiveData<T> {
    private static final String TAG = "hacket";
    private final AtomicBoolean mPending = new AtomicBoolean(false);
    @MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        if (hasActiveObservers()) {
            Log.w(TAG, "Multiple observers registered but only one will be notified of changes.");
        }
        // Observe the internal MutableLiveData
        super.observe(owner, new Observer<T>() {
            @Override
            public void onChanged(@Nullable T t) {
                if (mPending.compareAndSet(true, false)) {
                    Log.w(TAG, "onChanged observer=" + observer);
                    observer.onChanged(t);
                }
            }
        });
    }
    @MainThread
    public void setValue(@Nullable T t) {
        mPending.set(true);
        super.setValue(t);
    }
    /**
     * Used for cases where T is Void, to make calls cleaner.
     */
    @MainThread
    public void call() {
        setValue(null);
    }
}
Event(事件包装类)
  • Event 事件包装类特性
1
2
1. 解决了数据倒灌问题,没有解决数据黏性问题
2. 只允许一个消费者消费事件;如果要支持多个消费者,后续的消费者需要调用peekContent()
  • Event 事件包装类原理
    对 LiveData 要发送的数据包装成 Event;在收到 LiveData 更新时,第一个访问时,通过 Event 的 hasBeenHandled 变量置为 true 表示已经处理了,后续的 Observer 访问该变量就是 true,直接返回 null,即可实现一次性事件。
  • 事件包装类源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
 * https://medium.com/androiddevelopers/livedata-with-snackbar-navigation-and-other-events-the-singleliveevent-case-ac2622673150
 *
 * 事件包装
 *
 * Used as a wrapper for data that is exposed via a LiveData that represents an event.
 */
open class Event<out T>(private val content: T) {
    var hasBeenHandled = false
        private set // Allow external read but not write

    /**
     * Returns the content and prevents its use again.
     */
    fun getContentIfNotHandled(): T? {
        return if (hasBeenHandled) {
            null
        } else {
            hasBeenHandled = true
            content
        }
    }
    /**
     * Returns the content, even if it's already been handled.
     */
    fun peekContent(): T = content
}

使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
class ListViewModel : ViewModel {
    private val _navigateToDetails = MutableLiveData<Event<String>>()
    val navigateToDetails : LiveData<Event<String>>
        get() = _navigateToDetails
    fun userClicksOnButton(itemId: String) {
        _navigateToDetails.value = Event(itemId)  // Trigger the event by setting a new Event as a new value
    }
}
myViewModel.navigateToDetails.observe(this, Observer {
    it.getContentIfNotHandled()?.let { // Only proceed if the event has never been handled
        startActivity(DetailsActivity...)
    }
})

[译] 在 SnackBar,Navigation 和其他事件中使用 LiveData(SingleLiveEvent 案例)

EventLiveData(typealias 封装事件包装类)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/**
 * https://medium.com/androiddevelopers/livedata-with-snackbar-navigation-and-other-events-the-singleliveevent-case-ac2622673150
 *
 * 事件包装
 *
 * Used as a wrapper for data that is exposed via a LiveData that represents an event.
 */
open class Event<out T>(private val content: T) {

    var hasBeenHandled = false
        private set // Allow external read but not write

    /**
     * Returns the content and prevents its use again.
     */
    fun getContentIfNotHandled(): T? {
        return if (hasBeenHandled) {
            null
        } else {
            hasBeenHandled = true
            content
        }
    }

    /**
     * Returns the content, even if it's already been handled.
     */
    fun peekContent(): T = content
}

// 为 LiveData<Event<T>>提供类型别名,使用 EventLiveData<T> 即可
typealias EventMutableLiveData<T> = MutableLiveData<Event<T>> // ktlint-disable experimental:type-parameter-list-spacing

typealias EventLiveData<T> = LiveData<Event<T>> // ktlint-disable experimental:type-parameter-list-spacing

fun <T> EventMutableLiveData<T>.setValueEvent(value: T) {
    this.value = Event(value)
}

fun <T> EventMutableLiveData<T>.postValueEvent(value: T) {
    this.postValue(Event(value))
}

inline fun <T> EventLiveData<T>.observeEvent(
    owner: LifecycleOwner,
    crossinline onChanged: (T) -> Unit
) {
    observe(owner) {
        it.getContentIfNotHandled()?.let(onChanged)
    }
}

使用:

1
2
3
4
5
6
7
8
9
10
11
class StickyViewModel : ViewModel() {
    val eventLiveData = EventMutableLiveData<Int>()
    fun update(value: Int) {
        eventLiveData.setValueEvent(value)
    }
}

// 订阅
viewModel.eventLiveData.observeEvent(this) { t ->
    // ...
}
” 反射干预 LastVersion”
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private void considerNotify(ObserverWrapper observer) {
    if (!observer.mActive) {
        return;
    }
    if (!observer.shouldBeActive()) {
        observer.activeStateChanged(false);
        return;
    }
    if (observer.mLastVersion >= mVersion) {
        return;
    }
    observer.mLastVersion = mVersion;
    //noinspection unchecked
    observer.mObserver.onChanged((T) mData);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class SmartLiveData<T> : MutableLiveData<T>() {
    override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
        super.observe(owner, observer)
        // get livedata version
        val livedataVersion = javaClass.superclass.superclass.getDeclaredField("mVersion")
        livedataVersion.isAccessible = true
        // 获取livedata version的值
        val livedataVerionValue = livedataVersion.get(this)
        // 取 mObservers Filed
        val mObserversFiled = javaClass.superclass.superclass.getDeclaredField("mObservers")
        mObserversFiled.isAccessible = true
        // 取 mObservers 对象
        val objectObservers = mObserversFiled.get(this)
        // 取 mObservers 对象 所属的class SafeIterableMap
        val objectObserversClass = objectObservers.javaClass
        val methodGet = objectObserversClass.getDeclaredMethod("get", Any::class.java)
        methodGet.isAccessible = true
        // LifecycleBoundObserver
        val objectWrapper = (methodGet.invoke(objectObservers, observer) as Map.Entry<*, *>).value
        // ObserverWrapper
        val mLastVersionField = objectWrapper!!.javaClass.superclass.getDeclaredField("mLastVersion")
        mLastVersionField.isAccessible = true
        // 将 mVersion的值 赋值给 mLastVersion 使其相等
        mLastVersionField.set(objectWrapper, livedataVerionValue)
        Log.w("hacket", "SmartLiveData observe, observer=$observer")
    }
}

貌似并没有什么用,修改 version 是在 observe 后,此时已经收到了之前 Observable 的值了,解决不了数据倒灌问题。

UnPeek-LiveData (解决黏性和数据倒灌问题)
  • UnPeek-LiveData 特性:
1
2
3
1. 解决了黏性事件和数据倒灌问题
2. 适用于一次性事件通知,即MVI的Event订阅(Event更多的是该事件是一次的,用完就丢弃)
3. 不适用于MVI的State订阅(State关心的是最新的数据)
  • UnPeek-LiveData 原理:
1
2
3
4
1. UnPeekLiveData内部维护了一个mCurrentVersion版本,默认为-1
2. ObserverWrapper(Observer包装成了ObserverWrapper,里面维护了一个version)在observe时带了ProtectedUnPeekLiveData的mCurrentVersion,即observer时Observer和UnPeekLiveData的version是一样的
3. 当UnPeekLiveData更新数据时,ObserverWrapper回调了onChanged,此时判断UnPeekLiveData的version比ObserverWrapper的version大才更新数据,否则不更新,这样就避免了首次observe还会收到旧数据的黏性问题(对应的数据倒灌也不存在了)。
4. UnPeekLiveData在setValue/postValue会将mCurrentVersion版本加1,这样UnPeekLiveData的version就比ObserverWrapper的版本大了,那么ObserverWrapper就可以收到最新值了
  • UnPeek-LiveData 实现源码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
public class ProtectedUnPeekLiveData<T> extends LiveData<T> {
  private final static int START_VERSION = -1;
  private final AtomicInteger mCurrentVersion = new AtomicInteger(START_VERSION);
  protected boolean isAllowNullValue;
  
  @Override
  public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
    super.observe(owner, createObserverWrapper(observer, mCurrentVersion.get()));
  }
  @Override
  public void observeForever(@NonNull Observer<? super T> observer) {
    super.observeForever(createObserverWrapper(observer, mCurrentVersion.get()));
  }
  public void observeSticky(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
    super.observe(owner, createObserverWrapper(observer, START_VERSION));
  }
  public void observeStickyForever(@NonNull Observer<? super T> observer) {
    super.observeForever(createObserverWrapper(observer, START_VERSION));
  }
  @Override
  protected void setValue(T value) {
    mCurrentVersion.getAndIncrement();
    super.setValue(value);
  }
  class ObserverWrapper implements Observer<T> {
    private final Observer<? super T> mObserver;
    private int mVersion = START_VERSION;

    public ObserverWrapper(@NonNull Observer<? super T> observer, int version) {
      this.mObserver = observer;
      this.mVersion = version;
    }

    @Override
    public void onChanged(T t) {
      if (mCurrentVersion.get() > mVersion && (t != null || isAllowNullValue)) {
        mObserver.onChanged(t);
      }
    }

    @SuppressWarnings("unchecked")
    @Override
    public boolean equals(Object o) {
      if (this == o) {
        return true;
      }
      if (o == null || getClass() != o.getClass()) {
        return false;
      }
      ObserverWrapper that = (ObserverWrapper) o;
      return Objects.equals(mObserver, that.mObserver);
    }

    @Override
    public int hashCode() {
      return Objects.hash(mObserver);
    }
  }
  @Override
  public void removeObserver(@NonNull Observer<? super T> observer) {
    if (observer.getClass().isAssignableFrom(ObserverWrapper.class)) {
      super.removeObserver(observer);
    } else {
      super.removeObserver(createObserverWrapper(observer, START_VERSION));
    }
  }

  private ObserverWrapper createObserverWrapper(@NonNull Observer<? super T> observer, int version) {
    return new ObserverWrapper(observer, version);
  }
  public void clear() {
    super.setValue(null);
  }
}

参考:ProtectedUnPeekLiveData.java

MutableSharedFlow
1
2
3
4
5
6
7
8
class ListViewModel : ViewModel() {
    val _navigateToDetails = MutableSharedFlow<Boolean>()
    fun userClicksOnButton() {
        viewModelScope.launch {
            _navigateToDetails.emit(true)
        }
    }
}

SharedFlow 这个热流

1
2
3
4
5
public fun <T> MutableSharedFlow(
    replay: Int = 0,
    extraBufferCapacity: Int = 0,
    onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND
): MutableSharedFlow<T>
  • 参数 replay: 当有新的订阅者 collect 的时候(可以理解为 collect 就是 Livedata 中的 observe),发送几个 (replay)collect 之前已经发送过的数据给它,默认值是 0,就是不会收到之前的消息的;replay 改成 1 就和 LiveData 效果一样

LiveData 数据黏性和数据倒灌解决方案小结

 黏性问题数据倒灌特点原理
SingleLiveEvent×能解决数据倒灌,解决不了黏性问题;Google 出品;只有第一个 Observer 订阅后能收到更新数据,后续的 Observer 订阅收不到SingleLiveEvent 维护一个默认值为 false 的 mPengding,onChanged 更新时,不是 true,就收不到数据回调;setValue 更新 mPending 为 true
Event 事件包装类×能解决数据倒灌,解决不了黏性问题;和 SingleLiveEvent 一样只能单个 Observer 消费事件,多个 Observer 订阅调用 peekContent() 可返回已经消费的事件对 LiveData 发射的数据进行 Event 包装,通过 Event 内部变量 hasBeenHandled 控制只有首次访问的 Observer 能获取到该事件,从而保证事件的一次性
EventLiveData(typealis Event 事件包装)×同 Event 事件包装类同 Event 事件包装类
UnpeekLiveData解决数据倒灌和黏性问题UnpeekLiveData 维护了一个 version,Observer 也维护了一个 version,默认两个相等,只有 UnpeekLiveData 的 version 大于 Observer 的 version 时,Observer 才能收到更新;setValue 时 UnpeekLiveData 的 version 会自增
MutableSharedFlow能解决黏性和数据倒灌问题replay=0;replay=1 就和 LiveData 一样

Ref

LiveData 小结

0、什么是 LiveData?

  1. LiveData 是可感知生命周期的,可观察的,数据持有者。
  2. 观察者的回调在主线程
  3. Observer 在 LifecycleOwner 处于 DESTROY 时订阅自动取消
  4. LifecycleOwner 不在 active(state>=STARTED)时,不更新回调 onChanged;在 active 时,回调 onChanged

1、LiveData 的实现原理?

  1. 在 LiveData.observe 的时,注册了一个 LifecycleObserver(包装成了 LifecycleBoundObserver),就可以监听到 LifecycleOwner 的生命周期变化的回调 (onStateChanged())。
  2. 在 onStateChanded 中,如果发现 state 为 Lifecycle.State.DESTROYED 的话,会直接 removeObserver;如果是其他状态,调用 activeStateChanged() 然后 dispatchingValue() 分发 value
  3. dispatchingValue 中遍历所有的 Observer,调用其 onChanged() 方法

2、LiveData postValue 数据丢失的问题 (postValue 会有数据丢失)?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
static final Object NOT_SET = new Object();
volatile Object mPendingData = NOT_SET;
final Object mDataLock = new Object();
// If you called this method multiple times before a main thread executed a posted task,
// only the last value would be dispatched.
protected void postValue(T value) {
    boolean postTask;
    synchronized (mDataLock) {
        postTask = mPendingData == NOT_SET; // 首次postValue时postTask为true
        mPendingData = value;
    }
    if (!postTask) { // 首次postValue postTask为true,不会return;多次post
        return;
    }
    // 通过Handler提交一个Runable
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
private final Runnable mPostValueRunnable = new Runnable() {
    @Override
    public void run() {
        Object newValue;
        synchronized (mDataLock) {
            newValue = mPendingData;
            mPendingData = NOT_SET;
        }
        setValue((T) newValue);
    }
};
  1. 在提交的 postValue 的 Runnable 未执行前,如果调用多次 postValue,只有最新的 value 会被分发
  2. 多次提交 postValue,只会更新 mPendingData,导致多次提交只有最后一次的 value 有效
  3. 或者说 postValue 会有数据的丢失,其实就是在提交的 Runnable 未执行时,多次 postValue,只有最后一次 postValue 的值会存在,前面提交的值就被覆盖掉了,表现看起来就是丢失了

3、LiveData 黏性问题?黏性问题的原理?怎么解决黏性问题?

什么是 LiveData 的黏性问题?
LiveData setValue 一次值后,后续有新的 Observer 注册时,会直接把之前的旧值给这个 Observer
为什么要设计成这样?
LiveData 的设计也不是为了传递一次性事件的,它是为了反应 View 当前状态的,View 层只需要根据当前 LiveData 的值去渲染数据就行,非激活状态时 View 都不可见,就算更新了也没意义,只关心最新的值。
黏性问题的原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// LiveData Android29
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) { // 1
        return;
    }
    observer.mLastVersion = mVersion;
    observer.mObserver.onChanged((T) mData);
}

首次订阅的 Observer 的 mLastVersion=-1,不会大于等于 mVersion,导致 Observer 的 onChanged 会执行
黏性问题的解决

  1. Google 官方的 SingleLiveEvent

只适用于单个 Observer 的订阅,多个 Observer 订阅,只有第一个 Observer 能收到值;只适用于只有首个 Observer 可以收到更新值的场景

  1. UnPeek-LiveData
  • UnPeekLiveData 维护了一个 version,默认值 -1;
  • UnPeekLiveData setValue 时 versions 自增
  • observe() 方法中,会把 UNPeekLiveData 的 version 带给 Observer
  • 自定义一个 ObserverWrapper ,包装了传递进来的 Observer,里面也维护了一个 mVersion ,默认值 -1
  • 在 ObserverWrapper 的 onChanged() 方法中判断,只有 UnPeekLiveData.version>mVerion,才执行 Observer 的 onChanged() 方法;在未 setValue 情况下,新 observe 的 Observer 的 version 就和 UnPeekLiveData 的 version 一样了,这样就避免了数据的倒灌

c3feo

4、LiveData setValue 和 postValue 的区别

1
2
liveData.postValue("a");
liveData.setValue("b");

先输出 b 再输出 a

本文由作者按照 CC BY 4.0 进行授权