Kotlin 监听数据变化的几种方案

2,615 阅读2分钟

在 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​否

参考文档