在 MVVM 和 MVI 架构中,我们要监听 Model/State 某个属性值的变化,然后通知对应的监听器更新视图。
我们将从生命周期、嵌套属性、属性指针重定向、横竖屏切换、对集合的兼容性、对列表的兼容性和性能等多个维度对比如下候选方案优劣。
假设我们有数据类,我们要监听 Person&Address 中属性的变更:
data class Person(var name: String, var addr: Address?)
data class Address(var street: String, var district: String)
方案一:原生 set&get
我们知道 Kotlin 属性天然携带 set() 和/或 get() 方法,只读属性(val 修饰)仅有 get() 方法,而可读写属性(var 修饰)既有 set() 又有 get() 方法。我们可以通过重写 set&get 方法来监听属性变更:
open class BaseModel {
val listeners = mutableMapOf<String, Observer<Any?>>()
fun <V> registerListener(prop: KProperty<V>, observer: Observer<Any?>) {
listeners[prop.name] = observer
}
fun <V> unregisterListener(prop: KProperty<V>) {
listeners.remove(prop.name)
}
}
class Person : BaseModel() {
var firstName: String = ""
set(value) {
listeners["firstName"]?.onChanged(value)
}
var addr: Address? = null
set(value) {
listeners["addr"]?.onChanged(value)
}
}
方案二:Delegate.observable()
另外,Kotlin 还通过 by 关键字提供了属性代理,在代理中获取变更通知。observale() 和 vetoable() 的区别在于前者是在属性更新后得到通知,而后者是在属性更新前就获得通知,并且可以通过返回值来决定该次更新是否生效(true 生效,false 忽略该次更新)。
代码块
class Person : BaseModel() {
var firstName: String by Delegates.observable("") { property, oldValue, newValue ->
listeners[property.name]?.onChanged(newValue)
}
var addr: Address? by Delegates.vetoable(Address()) { property, oldValue, newValue ->
listeners[property.name]?.onChanged(newValue)
true
}
}
方案三:LiveData+KProperty
//监听一个属性
fun <T, A> LiveData<T>.observeState(
lifecycleOwner: LifecycleOwner,
prop1: KProperty1<T, A>,
action: (A) -> Unit
) {
this.map {
StateTuple1(prop1.get(it))
}.distinctUntilChanged().observe(lifecycleOwner) { (a) ->
action.invoke(a)
}
}
//监听两个属性
fun <T, A, B> LiveData<T>.observeState(
lifecycleOwner: LifecycleOwner,
prop1: KProperty1<T, A>,
prop2: KPro
方案对比
| 方案 | 支持生命周期 | 支持嵌套属性 | 支持属性指针重定向 | 性能 |
|---|---|---|---|---|
| 原生 set&get | 否 | 是 | ||
| Delegate.observable | 否 | 是 | ||
| LiveData+KProperty | 是 | 否 | ||
| StateFlow | 否 |