From de88ecac4f06491a30158e4bf738d4debcf0e067 Mon Sep 17 00:00:00 2001 From: Michal Zielinski Date: Fri, 18 Oct 2024 08:08:56 -0700 Subject: [PATCH] Integrate custom scoped pooling with ComposeComponent Summary: As per title - in this diff we're making `ComposeComponent` to use custom scoped pooling. It'll dispose composition when the `ComposeView` is discarded from the pool. Reviewed By: kingsleyadio Differential Revision: D64236148 fbshipit-source-id: 680fa599f0082147eb9d82903bb5b7a724e500dd --- .../src/main/kotlin/com/facebook/litho/BUCK | 2 + .../com/facebook/litho/ComposeComponent.kt | 15 +++-- ...ComposeComponentViewCompositionStrategy.kt | 58 +++++++++++++++++++ litho-core/src/main/res/values/ids.xml | 1 + .../litho/widget/LithoRecyclerView.java | 2 + 5 files changed, 74 insertions(+), 4 deletions(-) create mode 100644 litho-compose/src/main/kotlin/com/facebook/litho/ComposeComponentViewCompositionStrategy.kt diff --git a/litho-compose/src/main/kotlin/com/facebook/litho/BUCK b/litho-compose/src/main/kotlin/com/facebook/litho/BUCK index 3cc6423356..b41439622a 100644 --- a/litho-compose/src/main/kotlin/com/facebook/litho/BUCK +++ b/litho-compose/src/main/kotlin/com/facebook/litho/BUCK @@ -20,6 +20,7 @@ load( "LITHO_COMPOSE_UI_TARGET", "LITHO_JAVA_TARGET", "LITHO_KOTLIN_STDLIB_TARGET", + "LITHO_RES_TARGET", "litho_android_library", "make_dep_path", ) @@ -44,6 +45,7 @@ litho_android_library( "//fbandroid/libraries/components/litho-widget/src/main/java/com/facebook/litho/widget:widget-bare", "//fbandroid/libraries/compose/view:view", LITHO_KOTLIN_STDLIB_TARGET, + LITHO_RES_TARGET, LITHO_COMPOSE_FOUNDATION_TARGET, LITHO_COMPOSE_RUNTIME_TARGET, LITHO_COMPOSE_UI_TARGET, diff --git a/litho-compose/src/main/kotlin/com/facebook/litho/ComposeComponent.kt b/litho-compose/src/main/kotlin/com/facebook/litho/ComposeComponent.kt index ad7f74f363..32ebb4efdb 100644 --- a/litho-compose/src/main/kotlin/com/facebook/litho/ComposeComponent.kt +++ b/litho-compose/src/main/kotlin/com/facebook/litho/ComposeComponent.kt @@ -52,7 +52,6 @@ class ComposeComponent( CompositionContextHelper.bind( content, requireNotNull(androidContext.findComponentActivity())) onUnbind { - content.disposeComposition() CompositionContextHelper.unbind(content, usedCustomCompositionContext) } } @@ -70,9 +69,17 @@ class ComposeComponent( companion object { @JvmField - val ALLOCATOR: ViewAllocator = ViewAllocator { context -> - MetaComposeView(context) - } + val ALLOCATOR: ViewAllocator = + ViewAllocator( + useCustomPoolScope = true, onDiscarded = { content -> content.disposeComposition() }) { + context -> + MetaComposeView(context).apply { + // Normally content shouldn't be modified within ViewAllocator, but in this case + // we want all MetaComposeViews used by ComposeComponent to use + // ComposeComponentViewCompositionStrategy + setViewCompositionStrategy(ComposeComponentViewCompositionStrategy) + } + } } } diff --git a/litho-compose/src/main/kotlin/com/facebook/litho/ComposeComponentViewCompositionStrategy.kt b/litho-compose/src/main/kotlin/com/facebook/litho/ComposeComponentViewCompositionStrategy.kt new file mode 100644 index 0000000000..d9d89c8f82 --- /dev/null +++ b/litho-compose/src/main/kotlin/com/facebook/litho/ComposeComponentViewCompositionStrategy.kt @@ -0,0 +1,58 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.facebook.litho + +import android.view.View +import androidx.compose.ui.platform.ViewCompositionStrategy.DisposeOnDetachedFromWindow +import androidx.core.view.ancestors +import com.facebook.compose.view.AbstractMetaComposeView +import com.facebook.compose.view.MetaViewCompositionStrategy + +/** + * The composition will be disposed automatically when the view is detached from a window, unless it + * is pooled by Litho. + * + * When not pooled by Litho, this behaves exactly the same as [DisposeOnDetachedFromWindow]. + */ +object ComposeComponentViewCompositionStrategy : MetaViewCompositionStrategy { + override fun installFor(view: AbstractMetaComposeView): () -> Unit { + val listener = + object : View.OnAttachStateChangeListener { + override fun onViewAttachedToWindow(v: View) = Unit + + override fun onViewDetachedFromWindow(v: View) { + if (!view.isWithinLithoPoolingContainer) { + view.disposeComposition() + } + } + } + view.addOnAttachStateChangeListener(listener) + + return { view.removeOnAttachStateChangeListener(listener) } + } +} + +/** Whether one of this View's ancestors has R.id.litho_pooling_container tag set to true. */ +val View.isWithinLithoPoolingContainer: Boolean + get() { + ancestors.forEach { + if (it is View && it.getTag(R.id.litho_pooling_container) == true) { + return true + } + } + return false + } diff --git a/litho-core/src/main/res/values/ids.xml b/litho-core/src/main/res/values/ids.xml index 6cbda083c6..340bcdc667 100644 --- a/litho-core/src/main/res/values/ids.xml +++ b/litho-core/src/main/res/values/ids.xml @@ -23,5 +23,6 @@ + diff --git a/litho-widget/src/main/java/com/facebook/litho/widget/LithoRecyclerView.java b/litho-widget/src/main/java/com/facebook/litho/widget/LithoRecyclerView.java index 7b6406206e..2b94da0cfd 100644 --- a/litho-widget/src/main/java/com/facebook/litho/widget/LithoRecyclerView.java +++ b/litho-widget/src/main/java/com/facebook/litho/widget/LithoRecyclerView.java @@ -22,6 +22,7 @@ import android.view.MotionEvent; import androidx.annotation.Nullable; import androidx.recyclerview.widget.RecyclerView; +import com.facebook.litho.R; import java.util.ArrayList; import java.util.List; @@ -48,6 +49,7 @@ public LithoRecyclerView(Context context, @Nullable AttributeSet attrs) { public LithoRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); + setTag(R.id.litho_pooling_container, true); } /**