Использование «Delegates.observable» со списком наблюдателей - PullRequest
0 голосов
/ 16 апреля 2020

В этой статье показан один пример кода с использованием нескольких обработчиков в одном наблюдателе. Я приведу пример кода из этой статьи ниже. Но в этом коде есть ошибка утечки памяти - обработчик добавляется в список непостоянных таблиц, но он не удаляется из списка, когда, например, объект, использующий один из обработчиков, удаляется из памяти.

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 вызывает много трудностей .

Ответы [ 2 ]

1 голос
/ 16 апреля 2020

Традиционно, когда что-то подписывается на что-то другое, оно отвечает за отмену подписки. Вы можете сделать это, используя 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, поэтому нам не придется беспокоиться о возможности двух разных наблюдателей, возможно, имеющих объектное равенство.

Если вы хотите автоматизировать отписку от рассылки, чтобы вам не приходилось беспокоиться об этом, когда ваш фрагмент или действие выходит из области видимости, вы можете потребовать, чтобы наблюдатели были 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")
}

0 голосов
/ 17 апреля 2020

Спасибо за ответ на Tenfour04! Я взял его ответ за основу и сделал простой универсальный класс, который ведет список наблюдателей. Класс поддерживает автоматическую отписку, если в качестве ключа используется 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")
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...