为什么 Google 选择将 LiveData 设计为粘性的?
2024-01-03 23:00:46
在软件开发领域,理解设计决策背后的原因至关重要,而 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 应用程序开发带来了显着的优势。通过理解其背后的原理和动机,我们能够充分利用这一特性来构建高度响应、易于维护的应用程序。