LiveData postValue会“丢”数据

本文作者:我的Android 翻山之路,原文发布于:Android 翻山之路。

做个实验

class MainActivity : AppCompatActivity() {
    private val liveData = MutableLiveData<Int>()

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

        // 订阅观察者
        liveData.observe(this) {
            // 控制台打印 观察者收到的值
            Log.d("MainActivity", "observer: $it")
        }

        // for 循环调用 postValue
        for (i in 0..2) {
            Log.d("MainActivity", "postValue: $i")
            liveData.postValue(i)
        }
    }
}
 

来看看控制台:

只有 for 循环最后一次 postValue 的值 2 打印了出来。为什么不是 0、1、2 都打印出来呢?

LiveData 有 setValue 和 postValue 两种通知观察者的方法,postValue 从上看有点问题?那我们来试试 setValue。

将上面的实验代码 postValue 改为 setValue:

class MainActivity : AppCompatActivity() {
    private val liveData = MutableLiveData<Int>()

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

        // 订阅观察者
        liveData.observe(this) {
            // 控制台打印 观察者收到的值
            Log.d("MainActivity", "observer: $it")
        }

        // for 循环调用 setValue
        for (i in 0..2) {
            liveData.setValue(i)
        }
    }
}
 

再看看控制台:

 

 

怎么也是这样呢。严重怀疑 setValue 和 postValue 有毛病,得治。

这两次实验都是在 onCreate 里完成的,要不要尝试把调用 postValue 和 setValue 的位置换一换呢,算了,说干就干:加一个按钮,点击按钮时再循环 postValue:

class MainActivity : AppCompatActivity() {

    private val liveData = MutableLiveData<Int>()
    private val btn_set_value: Button by lazy { findViewById(R.id.btn_set_value) }

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

        liveData.observe(this) {
            Log.d("MainActivity", "observer: $it")
        }

        btn_set_value.setOnClickListener {
            for (i in 0..2) {
                Log.d("MainActivity", "postValue: $i")
                liveData.postValue(i)
            }
        }
    }
}
 

点击按钮之后的控制台:

还有什么值得留恋。。。

洗把脸,清醒清醒postValue 不行,再试试 setValue。

class MainActivity : AppCompatActivity() {

    private val liveData = MutableLiveData<Int>()
    private val btn_set_value: Button by lazy { findViewById(R.id.btn_set_value) }

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

        liveData.observe(this) {
            Log.d("MainActivity", "liveData: $it")
        }

        btn_set_value.setOnClickListener {
            for (i in 0..10) {
                Log.d("MainActivity", "setValue: $i")
                liveData.setValue(i)
            }
        }
    }
}
 

小心翼翼的点击 button,如看考试成绩般一点点的拉开控制台:

 

可害的好苦啊,终于他变了,而我也变的情绪失控。

为什么!!!

 

小小总结一下

总结上述 4 种方式的实验结果如下:

1. onCreate 方法中调用 postValue 和 setValue 结果一样,observer 都是只收到了 for 循环最后一次的值。

2. 点击按钮时循环调用 postValue 和 setValue 时结果不一样,postValue 只收到了最后一次的值,而 setValue 则收到了所有 for 循环的值。

为什么会发生这样令人困惑的结果的呢?

一点点来看

先看下 LiveData.setValue 调用后,怎么到的 observe 的 observer.onChanged 方法

private int mVersion;
private volatile Object mData;

/**
 * Sets the value. If there are active observers, the value will be dispatched to them.
 * <p>
 * This method must be called from the main thread. If you need set a value from a background
 * thread, you can use {@link #postValue(Object)}
 *
 * @param value The new value
 */
@MainThread
protected void setValue(T value) {
    // 必须主线程调用
    assertMainThread("setValue");
    // LiveData 的成员属性 mVersion 数据版本号 +1
    mVersion++;
    // 将 value 赋值给 LiveData 的成员属性 mData;
    mData = value;
    // 分发 value
    dispatchingValue(null);
}

void dispatchingValue(@Nullable ObserverWrapper initiator) {
    if (mDispatchingValue) {
        mDispatchInvalidated = true;
        return;
    }
    mDispatchingValue = true;
    do {
        mDispatchInvalidated = false;
        if (initiator != null) {
            considerNotify(initiator);
            initiator = null;
        } else {
            // initiator 为 null 所以会走到这里
            // 遍历观察者
            for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
                    mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                // 通知
                considerNotify(iterator.next().getValue());
                if (mDispatchInvalidated) {
                    break;
                }
            }
        }
    } while (mDispatchInvalidated);
    mDispatchingValue = false;
}

// ObserverWrapper 是 Observer 的包装类,这里不再延伸
private void considerNotify(ObserverWrapper observer) {
    // 判断 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;
    }
    // 校验 Observer 的版本号是否小于 LiveData 的版本号,版本号是 LiveData 粘性的关键
    if (observer.mLastVersion >= mVersion) {
        return;
    }
    // LiveData 版本号赋值给 Observer 的版本号
    observer.mLastVersion = mVersion;
    // 这里这里 终于是看到了 将缓存的值 mData 传递给了 Observer 的 onChanged 方法。也就是 LiveData.observe 传入对象 Observer 的 onChanged 方法。
    observer.mObserver.onChanged((T) mData);
}

看完上面代码的注释我们知道了调用 setValue 后,到 Observer 的 onChanged 方法被调用的过程。

中间有个关键点,最后 considerNotify 方法里会判断 !observer.mActive 。判断 observer 是否在活跃状态,不活跃则不往下走。

来看看什么是活跃,什么是不活跃。

// ObserverWrapper 是 Observer 的包装类
private abstract class ObserverWrapper {
    // LiveData.observe 方法传入的 Observer 对象
    final Observer<? super T> mObserver;
    // 这是我们要分析的 是否活跃的成员属性
    boolean mActive;
    int mLastVersion = START_VERSION;

    ObserverWrapper(Observer<? super T> observer) {
        mObserver = observer;
    }

    abstract boolean shouldBeActive();

    boolean isAttachedTo(LifecycleOwner owner) {
        return false;
    }

    void detachObserver() {
    }

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

activeStateChanged 方法又是在哪儿调用的呢?

需要先简单看下 LiveData 的 observe 方法:

@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
    assertMainThread("observe");
    if (owner.getLifecycle().getCurrentState() == DESTROYED) {
        // ignore
        return;
    }
    // 创建了 LifecycleBoundObserver 对象,传入我们的 Observer 对象到构造函数
    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");
    }
    if (existing != null) {
        return;
    }
    // 将 LifecycleBoundObserver 对象添加观察 Lifecycle 生命周期
    owner.getLifecycle().addObserver(wrapper);
}

// LifecycleBoundObserver 继承了 ObserverWrapper,并实现了 LifecycleEventObserver 接口用于观察生命周期事件。
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() {
        // 返回当前生命周期至少是 STARTED 状态才为 true,否则 false
        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;
            // 这里看到调用了父类 ObserverWrapper 的 activeStateChanged 对象,但是传入的值是 shouldBeActive 方法的返回值,shouldBeActive 方法也是父类 ObserverWrapper 的方法,但是是抽象方法,shouldBeActive 在 LifecycleBoundObserver 中实现在上面
            activeStateChanged(shouldBeActive());
            currentState = mOwner.getLifecycle().getCurrentState();
        }
    }

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

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

需要看 mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED) 方法怎么判断的。

/**
 * Started state for a LifecycleOwner. For an {@link android.app.Activity}, this state
 * is reached in two cases:
 * <ul>
 *     <li>after {@link android.app.Activity#onStart() onStart} call;
 *     <li><b>right before</b> {@link android.app.Activity#onPause() onPause} call.
 * </ul>
 */
STARTED,
 

看看注释是怎么解释的:

在两种情况下达到此状态:调用 onStart 后;就在 onPause 调用之前。众所周知安卓 Activity 生命周期如图:

现在解决一个困惑:为什么点击按钮时循环调用 setValue 时,收到了所有 for 循环的值。

这时候就可以解答一下了:

因为 LiveData 的观察生命周期的特性,只有在页面活跃状态下才可以分发值,当可以点击按钮时候,当前Activity 的生命周期已经走过了 OnStart、onResume,满足当前生命周期至少是 STARTED 的状态,setValue 到最终分发值一路畅通无阻,所以 for 循环中,每 set Value 一次,最终都会调用 Observer 的 onChanged 方法。

为什么在 onCreate 方法中循环调用 setValue 最终只通知了最后一个值呢?再回顾下 setValue 的流程:

private int mVersion;
private volatile Object mData;

/**
 * Sets the value. If there are active observers, the value will be dispatched to them.
 * <p>
 * This method must be called from the main thread. If you need set a value from a background
 * thread, you can use {@link #postValue(Object)}
 *
 * @param value The new value
 */
@MainThread
protected void setValue(T value) {
    // 必须主线程调用
    assertMainThread("setValue");
    // LiveData 的成员属性 mVersion 数据版本号 +1
    mVersion++;
    // 每次调用 setValue 都会将 value 赋值给 LiveData 的成员属性 mData;这里不会关心value 是否已经分发
    mData = value;
    // 分发 value
    dispatchingValue(null);
}

void dispatchingValue(@Nullable ObserverWrapper initiator) {
    ...
    // 通知
    considerNotify(iterator.next().getValue());
    ...
}

// ObserverWrapper 是 Observer 的包装类,这里不再延伸
private void considerNotify(ObserverWrapper observer) {
    // 判断 observer 是否在活跃状态,不活跃则不往下走,
    if (!observer.mActive) {
        return;
    }
    ...

    // 校验 Observer 的版本号是否小于 LiveData 的版本号,版本号是 LiveData 粘性的关键
    if (observer.mLastVersion >= mVersion) {
        return;
    }
    // LiveData 版本号赋值给 Observer 的版本号
    observer.mLastVersion = mVersion;
    // 这里这里 终于是看到了 将缓存的值 mData 传递给了 Observer 的 onChanged 方法。也就是 LiveData.observe 传入对象 Observer 的 onChanged 方法。
    observer.mObserver.onChanged((T) mData);
}

最终 considerNotify 方法中判断 !observer.mActive 条件满足,直接 return了,Observer 对象的 mLastVersion 也不会被赋值,始终为 -1。

因为在 onCreate 方法中调用的 setValue,observer.mActive 一定为 false, 而且最终 for 循环走完,LiveData 的 mVersion 为 2,mData 为 2,这解释了最终 LiveData 的 mData 值为 2,但是还不知道什么时候 Observer 的 onChanged 方法被调用。

上面分析我们知道,LiveData 调用 observe 时,observe 方法内,会将 Observer 对象包装为可感知生命周期的 LifecycleBoundObserver 对象,在 onStateChanged 中接受生命周期事件的改变,方法内调用 LifecycleBoundObserver 的父类的 activeStateChanged 方法,传入的是 shouldBeActive() 方法的返回值,而 shouldBeActive() 方法判断 isAtLeast(STARTED),也就是 调用 onStart 后;就在 onPause 调用之前 是返回 true。

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);
    // 如果从不活跃变为活跃状态 就会调用 dispatchingValue
    if (mActive) {
        dispatchingValue(this);
    }
}

这不就破案了吗

@SuppressWarnings("WeakerAccess") /* synthetic access */
void dispatchingValue(@Nullable ObserverWrapper initiator) {
    if (mDispatchingValue) {
        mDispatchInvalidated = true;
        return;
    }
    mDispatchingValue = true;
    do {
        mDispatchInvalidated = false;
        // 这里 initiator 不为null
        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;
}

@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.
    if (!observer.shouldBeActive()) {
        observer.activeStateChanged(false);
        return;
    }
    // 现在 observer 的 mLastVersion 值为 -1, 而 mVersion 值为 2,条件不通过
    if (observer.mLastVersion >= mVersion) {
        return;
    }
    observer.mLastVersion = mVersion;
    // 调用了 onChanged 传入了最新的值 mData 2
    observer.mObserver.onChanged((T) mData);
}
 

这下又解决了个疑问🤔️:onCreate 方法中循环调用 setValue,Observer 方法只收到了最后一次的值。

现在 setValue 的两次实验都能解释的清楚,剩下 postValue 的两种情况怎么解释呢?

再看下 LiveData.postValue 调用后,怎么到的 observe 的 observer.onChanged 方法

final Object mDataLock = new Object();
static final Object NOT_SET = new Object();
volatile Object mPendingData = NOT_SET;

/**
 * Posts a task to a main thread to set the given value. So if you have a following code
 * executed in the main thread:
 * <pre class="prettyprint">
 * liveData.postValue("a");
 * liveData.setValue("b");
 * </pre>
 * The value "b" would be set at first and later the main thread would override it with
 * the value "a".
 * <p>
 * If you called this method multiple times before a main thread executed a posted task, only
 * the last value would be dispatched.
 *
 * @param value The new value
 */
protected void postValue(T value) {
    boolean postTask;
    // 同步代码块 使用 mDataLock 对象当锁
    synchronized (mDataLock) {
        // 如果 mPendingData 等于 NOT_SET 值 postTask 才为 true
        postTask = mPendingData == NOT_SET;
        // postValue 传的值赋值给成员属性 mPendingData
        mPendingData = value;
    }
    // 若 postTask 为 false 则 return
    if (!postTask) {
        return;
    }
    // 发送一个消息到主线程 handler,
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}

// 主线程消息队列执行到 Message 时调用的 runnable
private final Runnable mPostValueRunnable = new Runnable() {
    @SuppressWarnings("unchecked")
    @Override
    public void run() {
        Object newValue;
        // 同步锁
        synchronized (mDataLock) {
            // 将 mPendingData 赋值给 newValue 对象
            newValue = mPendingData;
            // 将 mPendingData 对象值重置为 NOT_SET
            mPendingData = NOT_SET;
        }
        // 这里又调用了 setValue,之后就跟 LiveData.setValue 流程一样了。
        setValue((T) newValue);
    }
};

postValue 最终调用了 setValue,但是比 setValue 多了一步,就是往主线程消息队列插入了一条消息,等消息队列执行到这条消息时,才会调用 setValue。

onCreate 中 for 循环调用 postValue 为什么只有最后一个值输出呢?

分析一下下:我们知道 onCreate 方法是在主线程中被调用的(至于为啥可查询 Activity 启动流程,这里不分析),也是主线程消息队列的一条消息,消息队列会在上一条消息执行结束后才执行下一条消息。那么在 onCreate 中执行的 for 循环,是不是得等循环完毕才会往下走呢,答案当然是啦。再看这段代码:

final Object mDataLock = new Object();
static final Object NOT_SET = new Object();
volatile Object mPendingData = NOT_SET;

/**
 * Posts a task to a main thread to set the given value. So if you have a following code
 * executed in the main thread:
 * <pre class="prettyprint">
 * liveData.postValue("a");
 * liveData.setValue("b");
 * </pre>
 * The value "b" would be set at first and later the main thread would override it with
 * the value "a".
 * <p>
 * If you called this method multiple times before a main thread executed a posted task, only
 * the last value would be dispatched.
 *
 * @param value The new value
 */
protected void postValue(T value) {
    boolean postTask;
    // 同步代码块 使用 mDataLock 对象当锁
    synchronized (mDataLock) {
        // 如果 mPendingData 等于 NOT_SET 值 postTask 才为 true
        postTask = mPendingData == NOT_SET;
        // postValue 传的值赋值给成员属性 mPendingData
        mPendingData = value;
    }
    // 若 postTask 为 false 则 return
    if (!postTask) {
        return;
    }
    // 发送一个消息到主线程 handler,
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}

• for 循环第一次调用 postValue 值 0 时,LiveData 的 mPendingData = NOT_SET, postTask = mPendingData == NOT_SET 为 true,mPendingData 赋值为 value 0,下面判断条件 !postTask 为 false,往消息队列插入一条消息 Runable 对象为 mPostValueRunnable。

• for 循环第二次调用 postValue 值 1 时,LiveData 的 mPendingData = 0,postTask = mPendingData == NOT_SET 为 false,mPendingData 赋值为 value 1,下面判断条件 !postTask 为 true,直接 return 不会再向主线程发消息咯。

• for 循环第三次调用 postValue 值 2 时,LiveData 的 mPendingData = 1,postTask = mPendingData == NOT_SET 为 false,mPendingData 赋值为 value 2,下面判断条件 !postTask 为 true,直接 return 也不会再向主线程发消息。

到此 for 循环执行完毕,静等主线程执行完这条消息,等到执行 mPostValueRunnable 时:

// 主线程消息队列执行到 Message 时调用的 runnable
private final Runnable mPostValueRunnable = new Runnable() {
    @SuppressWarnings("unchecked")
    @Override
    public void run() {
        Object newValue;
        // 同步锁
        synchronized (mDataLock) {
            // 将 mPendingData 赋值给 newValue 对象
            newValue = mPendingData;
            // 将 mPendingData 对象值重置为 NOT_SET
            mPendingData = NOT_SET;
        }
        // 这里又调用了 setValue,之后就跟 LiveData.setValue 流程一样了。
        setValue((T) newValue);
    }
};

这时 mPendingData 的值为 2 !!!,又将 mPendingData 对象值重置为 NOT_SET,最后调用 setValue 方法将 2 传了进去。再之后就跟 setValue 逻辑一样了。

到这儿是不是解决了 postValue分别在 onCreate 和 点击事件 时循环调用只收到最后一个值的疑问🤔️呢?

 

转自:心态炸了,LiveData postValue会“丢”数据?

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值