返回

为什么 Google 选择将 LiveData 设计为粘性的?

Android

在软件开发领域,理解设计决策背后的原因至关重要,而 Google 对 LiveData 的粘性设计就是一个引人入胜的案例。

案例:见证 LiveData 的粘性

让我们从一个简单的示例开始。假设我们有一个 Activity,其中有一个文本框和一个按钮。当用户在文本框中输入文本并点击按钮时,文本将显示在 TextView 中。

class MainActivity : Activity() {

    private lateinit var editText: EditText
    private lateinit var textView: TextView
    private lateinit var button: Button

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

        editText = findViewById(R.id.editText)
        textView = findViewById(R.id.textView)
        button = findViewById(R.id.button)

        button.setOnClickListener {
            textView.text = editText.text.toString()
        }
    }
}

在这个例子中,当用户旋转设备或按后退键时,文本框中的文本将丢失。这是因为 Activity 在这些事件发生时会重新创建,而我们并没有在 onSaveInstanceState() 中保存文本。

揭秘粘性的秘密

为了解决这个问题,Google 引入了 LiveData。LiveData 是一种数据持有者,在 Activity 生命周期中是持久的。这意味着即使 Activity 被销毁并重新创建,LiveData 中存储的数据也不会丢失。

LiveData 的粘性是如何实现的呢?让我们潜入其源代码一探究竟。

class LiveData<T> : Observable<T>() {

    private val mObservers = HashMap<Observer<? super T>, ObserverWrapper<T>>()

    // ...

    override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
        val wrapper = ObserverWrapper(observer, true /* active */)
        mObservers[observer] = wrapper
        // ...
    }

    // ...
}

observe() 方法中,LiveData 将观察者存储在 mObservers 哈希映射中。值得注意的是,观察者包装器 (ObserverWrapper) 中有一个布尔标志 active,表示观察者是否处于活动状态。

class ObserverWrapper<T>(
    private val mObserver: Observer<in T>,
    var active: Boolean
) : Observer<T> {

    // ...

    override fun onChanged(t: T) {
        if (active) {
            mObserver.onChanged(t)
        }
    }

    // ...
}

Activity 销毁时,LiveData 将 active 标志设置为 false,这将阻止观察者在 Activity 重新创建时接收更新。

反射式验证

为了进一步证实 LiveData 的粘性,我们可以使用反射来操纵观察者包装器中的 active 标志。

class LiveDataHook {

    companion object {

        fun hookLiveData(liveData: LiveData<*>) {
            val field = liveData.javaClass.getDeclaredField("mObservers")
            field.isAccessible = true

            val observers = field.get(liveData) as HashMap<*, *>
            for (observer in observers.values) {
                val observerWrapper = observer as ObserverWrapper<*>
                observerWrapper.active = true
            }
        }
    }
}

通过反射,我们访问了 mObservers 哈希映射并手动设置所有观察者的 active 标志为 true。这有效地阻止了 LiveData 在 Activity 销毁时清除观察者。

Google 的设计动机

那么,为什么 Google 选择将 LiveData 设计为粘性的呢?原因有很多:

  • 提高响应能力: 粘性确保了当 Activity 重新创建时,UI 能够快速恢复其状态,从而提高了用户体验的响应能力。
  • 简化开发: 通过避免在 onSaveInstanceState()onRestoreInstanceState() 中保存和恢复状态的需要,LiveData 简化了应用程序开发。
  • 数据一致性: 粘性确保了所有观察者都始终接收最新的数据,无论 Activity 的生命周期如何。

结论

LiveData 的粘性设计是 Google 深思熟虑的决策,为 Android 应用程序开发带来了显着的优势。通过理解其背后的原理和动机,我们能够充分利用这一特性来构建高度响应、易于维护的应用程序。