Google为啥不自行判断线程从而分配setValue与postValue

157 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第5天,点击查看活动详情

前言

注意:您必须调用 setValue(T) 方法以从主线程更新 LiveData 对象。如果在工作器线程中执行代码,您可以改用 postValue(T) 方法来更新 LiveData 对象。

这段描述来自Android官方文档的 LiveData 概览,如果你熟悉这二者方法之间的区别,可以先查看使用setValue会报错,那就一直使用postValue行不行?这篇文章了解一下先。

因为今天又有一个新的思考点,那就是:为什么Google不自己进行判断线程,然后合理使用setValue()postValue()呢?。

实践

在开始实践前,我们先想一下,判断当前线程是否处于主线程中的代码应该怎么写呢?

答案在LiveData的源码中。

参考方法:

static void assertMainThread(String methodName) {
    if (!ArchTaskExecutor.getInstance().isMainThread()) {
        throw new IllegalStateException("Cannot invoke " + methodName + " on a background thread");
    }
}

public boolean isMainThread() {
    return Looper.getMainLooper().getThread() == Thread.currentThread();
}

根据当前线程的Looper对象与MainLooper进行对比,从而判断当前线程是否是主线程。

OK,接下来我们自己继承LiveData,实现一个可以自动识别当前线程,当方法处于子线程中就使用postValue,方法处于主线程中就使用setValue

public class LiMutableLiveData <T> extends LiveData<T> {
    private static final String TAG = "LiMutableLiveData";

    /**
     * Creates a MutableLiveData initialized with the given {@code value}.
     *
     * @param value initial value
     */
    public LiMutableLiveData(T value) {
        super(value);
    }

    /**
     * Creates a MutableLiveData with no value assigned to it.
     */
    public LiMutableLiveData() {
        super();
    }

    @Override
    public void postValue(T value) {
        Log.e(TAG, "postValue: ");
        super.postValue(value);
    }

    @Override
    public void setValue(T value) {
        Log.e(TAG, "setValue: ");
        super.setValue(value);
    }

    @SuppressLint("RestrictedApi")
    public void updateValue(T value) {
        Log.e(TAG, "addValue: value = " + value);
        if (!ArchTaskExecutor.getInstance().isMainThread()) {
            postValue(value);
        } else {
            setValue(value);
        }
    }
}

updateValue(T)方法中,我们先进行判断当前方法处于的线程,如果是主线程就调用setVaue()方法,反之处于子线程就调用postValue()方法。

然后我们测试一下:当我们点击开始按钮时,先在主线程中调用两次setValue()方法,然后再开启一个子线程来调用两次postValue()方法来更新数据。

class LiTestActivity : AppCompatActivity() {
    private val TAG = "LiTestActivity"

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

	liTestNumberLd.observe(this, {
            Log.e(TAG, "initObserve: liTestNumber = $it")
        })

        findViewById<Button>(R.id.startBtn).setOnClickListener {
            liTestNumberLd.updateValue(6)
            liTestNumberLd.updateValue(8)

            Thread {
                liTestNumberLd.updateValue(100)
                liTestNumberLd.updateValue(200)
            }.start()

        }

    }
}

接着看看输出日志是否如我们预期所想,输出日志如下:

E/LiMutableLiveData: updateValue: value = 6
E/LiMutableLiveData: setValue: 
E/LiTestActivity: initObserve: liTestNumber = 6
E/LiMutableLiveData: updateValue: value = 8
E/LiMutableLiveData: setValue: 
E/LiTestActivity: initObserve: liTestNumber = 8
E/LiMutableLiveData: updateValue: value = 100
E/LiMutableLiveData: postValue: 
E/LiMutableLiveData: updateValue: value = 200
E/LiMutableLiveData: postValue: 
E/LiMutableLiveData: setValue: 
E/LiTestActivity: initObserve: liTestNumber = 200

从日志中可以看出,updateValue()方法工作达到预期效果,当方法处于主线程中时就调用setValue()方法进行数据更新。反之,当方法处于子线程中时就调用postValue()方法进行数据更新,不过postValue()最终还是走setValue()

那为什么Google不这么设计呢?

很多时候也不是库做的事情越多越好吧,虽然LiveData可以做到自己判断一下当前方法所处的线程,然后合理调用setValue()方法与postValue()方法。但如果从性能的角度进行考虑,用户可以提前知道当前方法所处的线程,完全没必要做一次线程判断,所以Google这么做是从性能角度考虑?

其实我也不知道为啥Google要这么设计,屏幕前的你有任何想法吗?欢迎探讨。