Reputation: 977
This article shows one example code using multiple handlers in one observer. I give a code example from this article below. But there is a memory leak error in this code - the handler is added to the mutablelist, but it is not deleted from the list when, for example, an object using one of the handlers is deleted from memory.
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")
}
How to fix it, or are there alternative solutions? I need - so that when changing one property, several observers are invoked. Trying to use LiveData causes a lot of difficulties.
Upvotes: 1
Views: 1217
Reputation: 93609
Traditionally, when something subscribes to something else, it is responsible for unsubscribing itself. You could do this by using an 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.temperatureChangedObservers.add(this) { temperature ->
println("Temperature changed: $temperature")
}
// remove self as observer when going out of scope:
weatherStation.temperatureChangedObservers.remove(this)
I used IdentityHashMap rather than a MutableMap or HashMap so we won't have to worry about the possibility of two different observers possibly having object-equality.
If you want to automate unsubsribing, so you don't have to worry about it when your Fragment or Activity goes out of scope, you can require observers to be LifecycleOwners so you can observe their lifecycles. I didn't test this:
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")
}
Upvotes: 2
Reputation: 977
Thanks for the answer to the Tenfour04! I took his answer as a basis and made a simple universal class that maintains a list of observers. Class supports auto unsubsribing if the LifecycleOwner is used as a key. This is a simple alternative to 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")
}
Upvotes: 0