From a2ee68b445985f0e04efede08ec7120f71be0664 Mon Sep 17 00:00:00 2001 From: grechka62 Date: Mon, 21 Oct 2024 23:08:07 +0300 Subject: [PATCH] Fix memory leak in timers commit_hash:0c54576ac889e720ca8151d6d104c7a72a8056c9 --- CHANGELOG.md | 1 + .../div/core/timer/DivTimerEventDispatcher.kt | 31 +++++++++---------- .../yandex/div/core/timer/TimerController.kt | 13 ++++++-- 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7cab1622a..4b6f6f051 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ # Android Client: * `variable_triggers` defined locally are now attached only when they are within an active `state`. +* Fixed memory leak in timers. # iOS Client: * Added DivSubmitAction which allows to submit data from container. Can be used while creating forms with DivKit. diff --git a/client/android/div/src/main/java/com/yandex/div/core/timer/DivTimerEventDispatcher.kt b/client/android/div/src/main/java/com/yandex/div/core/timer/DivTimerEventDispatcher.kt index 7968bf7e6..76cd77df1 100644 --- a/client/android/div/src/main/java/com/yandex/div/core/timer/DivTimerEventDispatcher.kt +++ b/client/android/div/src/main/java/com/yandex/div/core/timer/DivTimerEventDispatcher.kt @@ -13,8 +13,6 @@ internal class DivTimerEventDispatcher( private var parentTimer: Timer? = null - private var div2View: Div2View? = null - fun getTimerController(id: String): TimerController? { return if (activeTimerIds.contains(id)) { timerControllers[id] @@ -33,9 +31,7 @@ internal class DivTimerEventDispatcher( timerControllers .filter { !ids.contains(it.key) } .values - .forEach { timerController -> - timerController.onDetach() - } + .forEach { it.reset() } activeTimerIds.clear() @@ -49,25 +45,26 @@ internal class DivTimerEventDispatcher( } fun onAttach(view: Div2View) { - val newParentTimer = Timer() - parentTimer = newParentTimer - - div2View = view + var newParentTimer: Timer? = null activeTimerIds.forEach { id -> - timerControllers[id]?.onAttach(view, newParentTimer) + val controller = timerControllers[id] ?: return@forEach + if (controller.isAttachedToView(view)) return@forEach + + (newParentTimer ?: createParentTimer().also { newParentTimer = it }).let { + controller.onAttach(view, it) + } } } - fun onDetach(view: Div2View) { - /* - * Fix bug, when div2view in recycler view. - * In recycler during bind first called attach for new view, than detach for old one. - */ - if (div2View != view) return + private fun createParentTimer() = Timer().also { + parentTimer?.cancel() + parentTimer = it + } + fun onDetach(view: Div2View) { timerControllers.values.forEach { timerController -> - timerController.onDetach() + timerController.onDetach(view) } parentTimer?.cancel() diff --git a/client/android/div/src/main/java/com/yandex/div/core/timer/TimerController.kt b/client/android/div/src/main/java/com/yandex/div/core/timer/TimerController.kt index 99c36a0f2..5eb3a629d 100644 --- a/client/android/div/src/main/java/com/yandex/div/core/timer/TimerController.kt +++ b/client/android/div/src/main/java/com/yandex/div/core/timer/TimerController.kt @@ -41,8 +41,8 @@ internal class TimerController( private fun updateTimer() { ticker.update( - divTimer.duration.evaluate(expressionResolver).toLong(), - divTimer.tickInterval?.evaluate(expressionResolver)?.toLong() + divTimer.duration.evaluate(expressionResolver), + divTimer.tickInterval?.evaluate(expressionResolver) ) } @@ -61,7 +61,12 @@ internal class TimerController( } } - fun onDetach() { + fun onDetach(view: Div2View?) { + if (view != div2View) return + reset() + } + + fun reset() { div2View = null ticker.saveState() @@ -70,6 +75,8 @@ internal class TimerController( savedForBackground = true } + fun isAttachedToView(view: Div2View) = view == div2View + fun applyCommand(command: String) { when (command) { START_COMMAND -> ticker.start()