Using setValue will report an error, so can you always use postValue?

Continue to create, accelerate growth! This is the first day of my participation in the "Nuggets Daily New Plan · June Update Challenge", click to view the details of the event

foreword

This article is a reflection on the method of updating LiveDatadata . I wonder if you have the same doubts in front of the screen?setValue()postValue()

What is the difference between postValue and setValue and how to use it?

It's described in LiveDatathe official documentation of: You must call  methods to update  objects [setValue(T)](<https://developer.android.com/reference/androidx/lifecycle/MutableLiveData#setValue(T)>) from the main thread  . LiveDataIf you execute code in a worker thread, you can instead use  [postValue(T)](<https://developer.android.com/reference/androidx/lifecycle/MutableLiveData#postValue(T)>) methods to update  LiveData objects.

That is to say, the main difference between these two methods is that one works on the main thread, while the other works on the child thread.

Take a look at their source code respectively:

@MainThread
protected void setValue(T value) {
    assertMainThread("setValue");
    mVersion++;
    mData = value;
    dispatchingValue(null);
}
复制代码

assertMainThread("setValue")The method is used to determine whether the current thread is in the main thread, if not, an exception will be thrown "Cannot invoke setValue on a background thread", that is, the method cannot be called in the child thread setValue().

If it is in the main thread, the latest data currently set is distributed to each observer object.

Then look at postValue()the source code of the method:

final Object mDataLock = new Object();

private volatile Object mData;
volatile Object mPendingData = NOT_SET;

private final Runnable mPostValueRunnable = new Runnable() {
    @SuppressWarnings("unchecked")
    @Override
    public void run() {
        Object newValue;
        synchronized (mDataLock) {
            newValue = mPendingData;
            mPendingData = NOT_SET;
        }
        setValue((T) newValue);
    }
};

protected void postValue(T value) {
    boolean postTask;
    synchronized (mDataLock) {
        postTask = mPendingData == NOT_SET;
        mPendingData = value;
    }
    if (!postTask) {
        return;
    }
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
复制代码

As long as child threads are involved, there are thread safety issues, postValue()no exception. So here it appears synchronized (mDataLock), lock the mDataLockobject , and use the volatilekeyword to modify the data object to mPendingDatabe sent to ensure timely visibility between threads, thereby ensuring thread safety.

And it can be seen from the source code that the data postValue()is essentially setValue()updated by switching to the main thread to execute setValue().

Is it OK to use postValue() all the time?

既然postValue()本质上也是通过setValue()来更新数据的,而且当你不小心在子线程中使用setValue()来更新数据时,会抛出异常,那这样的话,我可不可以一直使用postValue()来更新数据呢?

来个简单例子跑一下:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    ...省略代码...

    dbinding.apply {

        setValueBtn.setOnClickListener {
            for (setValueNum in 0..100) {
                Log.e("LiTest", "onCreate: setValueNum = $setValueNum")
                GlobalState.testNumberLd.value = setValueNum
            }
        }

        postValueBtn.setOnClickListener {
            for (postValueNum in 0..100) {
                Log.e("LiTest", "onCreate: postValueNum = $postValueNum")
                GlobalState.testNumberLd.postValue(postValueNum)
            }
        }

        GlobalState.testNumberLd.observe(this@TestHelperActivity, {
            Log.e("LiTest", "********* onCreate: observe number = $it", )
        })
    }

}
复制代码

我们先建一个Activity,放两个按钮,分别是setValueBtn按钮,用来调用setValue()方法,以及postValueBtn按钮,用来调用postValue()方法。点击按钮,分别进行100次数据更新。

思考一下,我们的观察者会分别观察到怎样的数据情况呢?

看看setValue()方法输出的日志情况:

E/LiTest: onCreate: setValueNum = 0
E/LiTest: ********* onCreate: observe number = 0
E/LiTest: onCreate: setValueNum = 1
E/LiTest: ********* onCreate: observe number = 1
E/LiTest: onCreate: setValueNum = 2
E/LiTest: ********* onCreate: observe number = 2
···
···
E/LiTest: onCreate: setValueNum = 99
E/LiTest: ********* onCreate: observe number = 99
E/LiTest: onCreate: setValueNum = 100
E/LiTest: ********* onCreate: observe number = 100
复制代码

每次进行setValue()更新数据后,我们的观察者都能接收到最新的数据。

再来看看postValue()方法输出的日志情况:

E/LiTest: onCreate: postValueNum = 0
E/LiTest: onCreate: postValueNum = 1
E/LiTest: onCreate: postValueNum = 2
···
···
E/LiTest: onCreate: postValueNum = 99
E/LiTest: onCreate: postValueNum = 100
E/LiTest: ********* onCreate: observe number = 100
复制代码

从日志可以看出,即使我们进行了100次postValue(),最终我们的观察者对象只观察到最后一次postValue()更新的数据。

这是当我们使用postValue()连续更新数据时,只有最后的数据会被更新,例如:

liveData.postValue("a");
liveData.postValue("b");
liveData.postValue("c");
复制代码

最终,活跃的观察者对象们只会收到数据"c",而感知不到"a""b"

总结

While we can always use postValue()methods to update data, it's not like setValue()methods throw exceptions, so in terms of ease of development, it postValue()'s easier to use methods to update data all the time. But it postValue()will lose data, and it needs to switch to the main thread to call the setValue()method to update the data, so in terms of performance, it will definitely cause more overhead. So we can't use postValue()methods to update data all the time, but we need to reasonably update data through methods in the main thread setValue(), and update data through methods in sub-threads postValue(), which also allows us to better clarify our code logic.

If this article is of any help to you, please give me a like for Le Li, thank you very much.

Guess you like

Origin juejin.im/post/7101670008350048286