使用 "Delegates.observable" 和观察者列表
Using "Delegates.observable" with list of Observers
此 article 显示了在一个观察者中使用多个处理程序的示例代码。我在下面的这篇文章中给出了一个代码示例。但是此代码中存在内存泄漏错误 - 处理程序已添加到可变列表中,但未从列表中删除,例如,使用其中一个处理程序的对象已从内存中删除。
class WeatherStation {
val temperatureChanged = mutableListOf<(Int) -> Unit>()
var temperature: Int by Delegates.observable(0) { _, _, newValue ->
temperatureChanged.forEach{it(newValue)}
}
}
// ...
val weatherStation = WeatherStation()
// Adding observer to the list, but where is its removal???
weatherStation.temperatureChanged.add { temperature ->
println("Temperature changed: $temperature")
}
如何解决,或者有其他解决方案吗?
我需要 - 以便在更改一个 属性 时调用多个观察者。
尝试使用 LiveData 会导致很多 .
传统上,当某个东西订阅了其他东西时,它负责自行取消订阅。您可以使用 IdentityHashMap 来做到这一点:
class WeatherStation {
val temperatureChangedObservers = IdentityHashMap<Any, (Int) -> Unit>()
var temperature: Int by Delegates.observable(0) { _, _, newValue ->
temperatureChangedObservers.values.forEach { it(newValue) }
}
}
// ...
val weatherStation = WeatherStation()
weatherStation.temperatureChanged.add(this) { temperature ->
println("Temperature changed: $temperature")
}
// remove self as observer when going out of scope:
weatherStation.remove(this)
我使用了 IdentityHashMap 而不是 MutableMap 或 HashMap,因此我们不必担心两个不同观察者可能具有对象相等性的可能性。
如果你想自动取消订阅,这样当你的 Fragment 或 Activity 超出范围时你就不必担心它,你可以要求观察者成为 LifecycleOwners 这样你就可以观察他们的生命周期。我没有测试这个:
class WeatherStation: LifecycleObserver {
private val temperatureChangedObservers = IdentityHashMap<LifecycleOwner, (Int) -> Unit>()
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun onObserverDestroyed(source: LifecycleOwner) {
temperatureChangedObservers.remove(source)
}
fun observeTemperature(observer: LifecycleOwner, action: (Int) -> Unit) {
temperatureChangedObservers[observer] = action
observer.lifecycle.addObserver(this)
}
var temperature: Int by Delegates.observable(0) { _, _, newValue ->
temperatureChangedObservers.values.forEach { it(newValue) }
}
}
// ...
val weatherStation = WeatherStation()
weatherStation.observeTemperature(this) { temperature ->
println("Temperature changed: $temperature")
}
感谢 Tenfour04!
我以他的回答为基础,做了一个简单的 universal class 来维护一个观察者列表。 Class 如果将 LifecycleOwner 用作密钥,则支持自动取消订阅。
这是 LiveData 的简单替代方案。
class Visor<T>(initialValue: T): LifecycleObserver {
private var value = initialValue
private val observers = WeakHashMap<Any, (T) -> Unit>()
fun subscribe(owner: Any, observer: (T) -> Unit) {
if (owner is LifecycleOwner)
owner.lifecycle.addObserver(this)
observers[owner] = observer
}
fun subscribeAndInvoke(owner: Any, observer: (T) -> Unit) {
add(owner, observer)
observer(value) // invoke
}
fun remove(key: Any) {
observers.remove(key)
}
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun onObserverDestroyed(owner: LifecycleOwner) {
remove(owner)
}
operator fun getValue(thisRef: Any?, prop: KProperty<*>): T = value
operator fun setValue(thisRef: Any?, prop: KProperty<*>, value: T) {
this.value = value
observers.values.forEach{it(value)} // invoke all observers
}
}
// example of using
class WeatherStation() {
var temperatureVisor = Visor<Int>(0)
var temperature: Int by temperatureVisor
// ...
}
// addition of the first observer
val weatherStation = WeatherStation()
weatherStation.temperatureVisor.subscribe(this) {
Log.d("Visor", "New temperature: $it")
}
此 article 显示了在一个观察者中使用多个处理程序的示例代码。我在下面的这篇文章中给出了一个代码示例。但是此代码中存在内存泄漏错误 - 处理程序已添加到可变列表中,但未从列表中删除,例如,使用其中一个处理程序的对象已从内存中删除。
class WeatherStation {
val temperatureChanged = mutableListOf<(Int) -> Unit>()
var temperature: Int by Delegates.observable(0) { _, _, newValue ->
temperatureChanged.forEach{it(newValue)}
}
}
// ...
val weatherStation = WeatherStation()
// Adding observer to the list, but where is its removal???
weatherStation.temperatureChanged.add { temperature ->
println("Temperature changed: $temperature")
}
如何解决,或者有其他解决方案吗?
我需要 - 以便在更改一个 属性 时调用多个观察者。
尝试使用 LiveData 会导致很多
传统上,当某个东西订阅了其他东西时,它负责自行取消订阅。您可以使用 IdentityHashMap 来做到这一点:
class WeatherStation {
val temperatureChangedObservers = IdentityHashMap<Any, (Int) -> Unit>()
var temperature: Int by Delegates.observable(0) { _, _, newValue ->
temperatureChangedObservers.values.forEach { it(newValue) }
}
}
// ...
val weatherStation = WeatherStation()
weatherStation.temperatureChanged.add(this) { temperature ->
println("Temperature changed: $temperature")
}
// remove self as observer when going out of scope:
weatherStation.remove(this)
我使用了 IdentityHashMap 而不是 MutableMap 或 HashMap,因此我们不必担心两个不同观察者可能具有对象相等性的可能性。
如果你想自动取消订阅,这样当你的 Fragment 或 Activity 超出范围时你就不必担心它,你可以要求观察者成为 LifecycleOwners 这样你就可以观察他们的生命周期。我没有测试这个:
class WeatherStation: LifecycleObserver {
private val temperatureChangedObservers = IdentityHashMap<LifecycleOwner, (Int) -> Unit>()
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun onObserverDestroyed(source: LifecycleOwner) {
temperatureChangedObservers.remove(source)
}
fun observeTemperature(observer: LifecycleOwner, action: (Int) -> Unit) {
temperatureChangedObservers[observer] = action
observer.lifecycle.addObserver(this)
}
var temperature: Int by Delegates.observable(0) { _, _, newValue ->
temperatureChangedObservers.values.forEach { it(newValue) }
}
}
// ...
val weatherStation = WeatherStation()
weatherStation.observeTemperature(this) { temperature ->
println("Temperature changed: $temperature")
}
感谢
class Visor<T>(initialValue: T): LifecycleObserver {
private var value = initialValue
private val observers = WeakHashMap<Any, (T) -> Unit>()
fun subscribe(owner: Any, observer: (T) -> Unit) {
if (owner is LifecycleOwner)
owner.lifecycle.addObserver(this)
observers[owner] = observer
}
fun subscribeAndInvoke(owner: Any, observer: (T) -> Unit) {
add(owner, observer)
observer(value) // invoke
}
fun remove(key: Any) {
observers.remove(key)
}
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun onObserverDestroyed(owner: LifecycleOwner) {
remove(owner)
}
operator fun getValue(thisRef: Any?, prop: KProperty<*>): T = value
operator fun setValue(thisRef: Any?, prop: KProperty<*>, value: T) {
this.value = value
observers.values.forEach{it(value)} // invoke all observers
}
}
// example of using
class WeatherStation() {
var temperatureVisor = Visor<Int>(0)
var temperature: Int by temperatureVisor
// ...
}
// addition of the first observer
val weatherStation = WeatherStation()
weatherStation.temperatureVisor.subscribe(this) {
Log.d("Visor", "New temperature: $it")
}