From 83ab07643551078611ee47e7a70fe07ac38fbcab Mon Sep 17 00:00:00 2001 From: grechka62 Date: Wed, 1 Apr 2026 12:08:02 +0300 Subject: [PATCH] Cleanup of rebind logic commit_hash:5c0c4cf137c47ab3b402a3ce3ea20a58937a293e --- .../yandex/div/core/state/DivStateManager.kt | 14 ++-- .../com/yandex/div/core/view2/Div2View.kt | 76 +++++++++---------- .../core/view2/Div2ViewReleaseChildrenTest.kt | 6 +- 3 files changed, 45 insertions(+), 51 deletions(-) diff --git a/client/android/div/src/main/java/com/yandex/div/core/state/DivStateManager.kt b/client/android/div/src/main/java/com/yandex/div/core/state/DivStateManager.kt index 7b1a2147d..f33299010 100644 --- a/client/android/div/src/main/java/com/yandex/div/core/state/DivStateManager.kt +++ b/client/android/div/src/main/java/com/yandex/div/core/state/DivStateManager.kt @@ -4,7 +4,6 @@ import androidx.annotation.AnyThread import androidx.collection.ArrayMap import com.yandex.div.DivDataTag import com.yandex.div.core.dagger.DivScope -import com.yandex.div.core.expression.asImpl import com.yandex.div.core.state.DivPathUtils.statePath import com.yandex.div.core.view2.BindingContext import com.yandex.div.data.Variable @@ -32,8 +31,8 @@ internal class DivStateManager @Inject constructor( private val states = ArrayMap() private val variables = mutableMapOf() - fun collectStateVariables(data: DivData, context: BindingContext) { - val cardVariables = variables.getOrPut(context.divView.dataTag.id) { mutableMapOf() } + fun collectStateVariables(tag: DivDataTag, data: DivData, context: BindingContext) { + val cardVariables = variables.getOrPut(tag.id) { mutableMapOf() } StateVariableCollector(cardVariables).collectStateVariables(data, context) } @@ -113,11 +112,12 @@ private class StateVariableCollector( fun collectStateVariables(data: DivData, context: BindingContext) = visit(data, context) - override fun defaultVisit(data: Div, context: BindingContext, path: DivStatePath) { - if (data !is Div.State) return - val resolver = context.expressionResolver.asImpl ?: return + override fun defaultVisit(data: Div, context: BindingContext, path: DivStatePath) = Unit + + override fun visit(data: Div.State, context: BindingContext, path: DivStatePath) { val variableName = data.value.stateIdVariable ?: return - val variable = resolver.getVariable(variableName) ?: return + val variable = context.expressionResolver.getVariable(variableName) ?: return variables.getOrPut(path.statePath) { StateVariableHolder(variable) } + super.visit(data, context, path) } } diff --git a/client/android/div/src/main/java/com/yandex/div/core/view2/Div2View.kt b/client/android/div/src/main/java/com/yandex/div/core/view2/Div2View.kt index 6aa9df1f0..8f96ab7cc 100644 --- a/client/android/div/src/main/java/com/yandex/div/core/view2/Div2View.kt +++ b/client/android/div/src/main/java/com/yandex/div/core/view2/Div2View.kt @@ -12,7 +12,6 @@ import android.view.MotionEvent import android.view.View import android.view.ViewGroup import androidx.annotation.VisibleForTesting -import androidx.core.view.ViewCompat import androidx.core.view.doOnAttach import androidx.core.view.isVisible import androidx.transition.Scene @@ -193,16 +192,16 @@ class Div2View private constructor( var prevDataTag: DivDataTag = DivDataTag.INVALID internal set - var divData: DivData? = null - internal set(value) { + private var _divData: DivData? = null + set(value) { field = value - updateRuntimeStore() updateTimers() bindingProvider.update(dataTag, field) } - private fun updateRuntimeStore(data: DivData? = divData, tag: DivDataTag = dataTag) { - data ?: return + val divData: DivData? get() = _divData + + private fun updateRuntimeStore(data: DivData, tag: DivDataTag) { oldRuntimeStore = runtimeStore runtimeStore = div2Component.runtimeStoreProvider.getOrCreate(tag, data, this) runtimeStore.updateSubscriptions() @@ -210,7 +209,7 @@ class Div2View private constructor( oldRuntimeStore?.clearBindings(this) } bindingContext = BindingContext(this, expressionResolver) - div2Component.stateManager.collectStateVariables(data, bindingContext) + div2Component.stateManager.collectStateVariables(tag, data, bindingContext) } private fun tryAttachVariableTriggers(data: DivData?) { @@ -360,22 +359,19 @@ class Div2View private constructor( ) val result = when { - oldData == null || data.allowsTransitionsOnDataChange(expressionResolver) -> { + oldData == null || data.allowsTransitionsOnDataChange(expressionResolver) -> updateNow(data, tag, reporter) - } - !isDataReplaceable - && isComplexRebindEnabled - && view.getChildAt(0) is ViewGroup - && complexRebind(data, oldData, reporter) - -> { - false - } + isDataReplaceable -> { - rebind(data, false, reporter) + rebind(data, reporter) false } + isComplexRebindEnabled && getChildAt(0) is ViewGroup && complexRebind(data, oldData, reporter) -> + false + else -> updateNow(data, tag, reporter) } + div2Component.divBinder.attachIndicators(this) sendCreationHistograms() @@ -399,7 +395,7 @@ class Div2View private constructor( tag: DivDataTag, paths: List, temporary: Boolean - ) = setDataWithStatesInternal(data, tag, paths, temporary) + ): Boolean = setDataWithStatesInternal(data, tag, paths, temporary) private fun setDataWithStatesInternal( data: DivData?, @@ -442,19 +438,17 @@ class Div2View private constructor( } val result = when { oldData == null -> updateNow(data, tag, reporter) - !isDataReplaceable - && isComplexRebindEnabled - && view.getChildAt(0) is ViewGroup - && complexRebind(data, oldData, reporter) - -> { - true - } + isDataReplaceable -> { - rebind(data, false, reporter) + rebind(data, reporter) true } + isComplexRebindEnabled && getChildAt(0) is ViewGroup && complexRebind(data, oldData, reporter) -> + true + else -> updateNow(data, tag, reporter) } + div2Component.divBinder.attachIndicators(this) sendCreationHistograms() notifyBindEnded() @@ -525,8 +519,8 @@ class Div2View private constructor( bindOnAttachRunnable?.cancel() val oldRootDiv = oldData.state()?.div val rootChanges = patch.changes.find { it.id == oldRootDiv?.value()?.id } ?: run { - rebind(oldData, false, reporter) - divData = newDivData + rebind(oldData, reporter) + _divData = newDivData div2Component.divBinder.setDataWithoutBinding(bindingContext, getChildAt(0), state.div) return true } @@ -553,12 +547,13 @@ class Div2View private constructor( bindingReporter ) return when { - !isDataReplaceable && isComplexRebindEnabled && view.getChildAt(0) is ViewGroup && - complexRebind(newDivData, oldData, bindingReporter) -> true isDataReplaceable -> { - rebind(newDivData, false, reporter) + rebind(newDivData, reporter) true } + isComplexRebindEnabled && getChildAt(0) is ViewGroup && + complexRebind(newDivData, oldData, bindingReporter) -> true + else -> updateNow(newDivData, dataTag, reporter) } } @@ -574,7 +569,7 @@ class Div2View private constructor( cleanup(removeChildren = false) dataTag = tag - divData = data + _divData = data val result = switchToDivData(oldData, data, reporter) @@ -635,7 +630,7 @@ class Div2View private constructor( } bindingsSnapshot.forEach { (view, div) -> view.bindingContext?.expressionResolver?.let { - if (ViewCompat.isAttachedToWindow(view)) { + if (view.isAttachedToWindow) { visibilityActionTracker.trackVisibilityActionsOf(this, it, view, div) } else { visibilityActionTracker.trackVisibilityActionsOf(this, it, null, div) @@ -789,7 +784,7 @@ class Div2View private constructor( } viewComponent.errorCollectors.getOrNull(dataTag, divData)?.cleanRuntimeWarningsAndErrors() layoutProviderBinder.release(divData) - divData = null + _divData = null dataTag = DivDataTag.INVALID } @@ -1182,7 +1177,7 @@ class Div2View private constructor( override fun hasScrollableViewUnder(event: MotionEvent): Boolean = hasScrollableChildUnder(event) - override fun getCurrentStateId() = stateId + override fun getCurrentStateId(): Long = stateId internal val currentRootPath: DivStatePath get() { @@ -1201,7 +1196,7 @@ class Div2View private constructor( } } - override fun getView() = this + override fun getView(): Div2View = this override fun getExpressionResolver(): ExpressionResolver { return runtimeStore.resolver @@ -1290,7 +1285,7 @@ class Div2View private constructor( viewToDivBindings.remove(view) } - private fun rebind(newData: DivData, isAutoanimations: Boolean, reporter: SimpleRebindReporter) { + private fun rebind(newData: DivData, reporter: SimpleRebindReporter) { try { if (childCount == 0) { reporter.onSimpleRebindNoChild() @@ -1305,13 +1300,10 @@ class Div2View private constructor( runBindingAction { histogramReporter.onRebindingStarted() viewComponent.errorCollectors.getOrNull(dataTag, divData)?.cleanRuntimeWarningsAndErrors() - divData = newData + _divData = newData div2Component.stateManager.updateState(dataTag, state.stateId, true) div2Component.divBinder.bind(bindingContext, getChildAt(0), state.div, DivStatePath.fromState(state)) requestLayout() - if (isAutoanimations) { - div2Component.divStateChangeListener.onDivAnimatedStateChanged(this) - } tryAttachVariableTriggers(newData) histogramReporter.onRebindingFinished() @@ -1334,7 +1326,7 @@ class Div2View private constructor( return@executeOnMainThreadBlocking false } histogramReporter.onRebindingStarted() - divData = newData + _divData = newData val task = this.rebindTask ?: createRebindTask(reporter).also { this.rebindTask = it diff --git a/client/android/div/src/test/java/com/yandex/div/core/view2/Div2ViewReleaseChildrenTest.kt b/client/android/div/src/test/java/com/yandex/div/core/view2/Div2ViewReleaseChildrenTest.kt index 9f2f9a312..66a744cd8 100644 --- a/client/android/div/src/test/java/com/yandex/div/core/view2/Div2ViewReleaseChildrenTest.kt +++ b/client/android/div/src/test/java/com/yandex/div/core/view2/Div2ViewReleaseChildrenTest.kt @@ -52,8 +52,10 @@ class Div2ViewReleaseChildrenTest { private val div2Context = spy(backingContext) { on { div2Component } doReturn component } - private val divView = Div2View(div2Context).apply { - divData = testData.data + private val divView = Div2View(div2Context) + + init { + divView.setData(testData.data, tag) } @Test