mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Delete pre-allocated views that were never mounted on the screen (#46473)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/46473 This diff extends the renderer of react native to ensure that pre-allocated views that were never mounted on the screen are deleted as soon as the shadow node is deleted from JS This feature is controlled by the ReactNativeFeatureFlag: enableDeletionOfUnmountedViews changelog: [internal] internal Reviewed By: javache Differential Revision: D62559190 fbshipit-source-id: 1af6785fc57256d12750db64489c9ecc6cf98c9d
This commit is contained in:
committed by
Facebook GitHub Bot
parent
68a92aa312
commit
6dbbbe33fd
@@ -2920,6 +2920,7 @@ public abstract interface class com/facebook/react/fabric/mounting/mountitems/Mo
|
||||
|
||||
public final class com/facebook/react/fabric/mounting/mountitems/MountItemFactory {
|
||||
public static final field INSTANCE Lcom/facebook/react/fabric/mounting/mountitems/MountItemFactory;
|
||||
public static final fun createDestroyViewMountItem (II)Lcom/facebook/react/fabric/mounting/mountitems/MountItem;
|
||||
public static final fun createDispatchCommandMountItem (IIILcom/facebook/react/bridge/ReadableArray;)Lcom/facebook/react/fabric/mounting/mountitems/DispatchCommandMountItem;
|
||||
public static final fun createDispatchCommandMountItem (IILjava/lang/String;Lcom/facebook/react/bridge/ReadableArray;)Lcom/facebook/react/fabric/mounting/mountitems/DispatchCommandMountItem;
|
||||
public static final fun createIntBufferBatchMountItem (I[I[Ljava/lang/Object;I)Lcom/facebook/react/fabric/mounting/mountitems/MountItem;
|
||||
|
||||
+8
@@ -760,6 +760,14 @@ public class FabricUIManager
|
||||
isLayoutable));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@AnyThread
|
||||
@ThreadConfined(ANY)
|
||||
private void destroyUnmountedView(int surfaceId, int reactTag) {
|
||||
mMountItemDispatcher.addMountItem(
|
||||
MountItemFactory.createDestroyViewMountItem(surfaceId, reactTag));
|
||||
}
|
||||
|
||||
@SuppressLint("NotInvokedPrivateMethod")
|
||||
@SuppressWarnings("unused")
|
||||
@AnyThread
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.fabric.mounting.mountitems
|
||||
|
||||
import com.facebook.react.fabric.mounting.MountingManager
|
||||
|
||||
/**
|
||||
* Destroyes the view asociated to the [reactTag] if exists. This MountItem is meant to be used ONLY
|
||||
* for views that were preallcated but never mounted on the screen.
|
||||
*/
|
||||
internal class DestroyUnmountedViewMountItem(
|
||||
private val _surfaceId: Int,
|
||||
private val reactTag: Int
|
||||
) : MountItem {
|
||||
|
||||
public override fun execute(mountingManager: MountingManager) {
|
||||
val surfaceMountingManager = mountingManager.getSurfaceManager(_surfaceId)
|
||||
if (surfaceMountingManager == null) {
|
||||
return
|
||||
}
|
||||
surfaceMountingManager.deleteView(reactTag)
|
||||
}
|
||||
|
||||
public override fun getSurfaceId(): Int = _surfaceId
|
||||
}
|
||||
+5
@@ -53,6 +53,11 @@ public object MountItemFactory {
|
||||
): MountItem =
|
||||
PreAllocateViewMountItem(surfaceId, reactTag, component, props, stateWrapper, isLayoutable)
|
||||
|
||||
/** @return a [MountItem] that will be used to destroy views */
|
||||
@JvmStatic
|
||||
public fun createDestroyViewMountItem(surfaceId: Int, reactTag: Int): MountItem =
|
||||
DestroyUnmountedViewMountItem(surfaceId, reactTag)
|
||||
|
||||
/**
|
||||
* @return a [MountItem] that will be read and execute a collection of MountItems serialized in
|
||||
* the int[] and Object[] received by parameter
|
||||
|
||||
@@ -532,6 +532,19 @@ void Binding::schedulerDidRequestPreliminaryViewAllocation(
|
||||
return;
|
||||
}
|
||||
mountingManager->maybePreallocateShadowNode(shadowNode);
|
||||
// Only the Views of ShadowNode that were pre-allocated (forms views) needs
|
||||
// to be destroyed if the ShadowNode is destroyed but it was never mounted
|
||||
// on the screen.
|
||||
if (ReactNativeFeatureFlags::enableDeletionOfUnmountedViews() &&
|
||||
shadowNode.getTraits().check(ShadowNodeTraits::Trait::FormsView)) {
|
||||
shadowNode.getFamily().onUnmountedFamilyDestroyed(
|
||||
[weakMountingManager =
|
||||
std::weak_ptr(mountingManager)](const ShadowNodeFamily& family) {
|
||||
if (auto mountingManager = weakMountingManager.lock()) {
|
||||
mountingManager->destroyUnmountedShadowNode(family);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Binding::schedulerDidDispatchCommand(
|
||||
|
||||
+16
-1
@@ -792,6 +792,21 @@ void FabricMountingManager::drainPreallocateViewsQueue() {
|
||||
}
|
||||
}
|
||||
|
||||
void FabricMountingManager::destroyUnmountedShadowNode(
|
||||
const ShadowNodeFamily& family) {
|
||||
auto tag = family.getTag();
|
||||
auto surfaceId = family.getSurfaceId();
|
||||
|
||||
// ThreadScope::WithClassLoader is necessary because
|
||||
// destroyUnmountedShadowNode is being called from a destructor thread
|
||||
facebook::jni::ThreadScope::WithClassLoader([&]() {
|
||||
static auto destroyUnmountedView =
|
||||
JFabricUIManager::javaClassStatic()->getMethod<void(jint, jint)>(
|
||||
"destroyUnmountedView");
|
||||
destroyUnmountedView(javaUIManager_, surfaceId, tag);
|
||||
});
|
||||
}
|
||||
|
||||
void FabricMountingManager::maybePreallocateShadowNode(
|
||||
const ShadowNode& shadowNode) {
|
||||
if (!shadowNode.getTraits().check(ShadowNodeTraits::Trait::FormsView)) {
|
||||
@@ -813,7 +828,7 @@ void FabricMountingManager::maybePreallocateShadowNode(
|
||||
preallocatedViewsQueue_.push_back(std::move(shadowView));
|
||||
} else {
|
||||
// Old implementation where FabricUIManager.preallocateView is called
|
||||
// immediatelly.
|
||||
// immediately.
|
||||
preallocateShadowView(shadowView);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,8 @@ class FabricMountingManager final {
|
||||
|
||||
void maybePreallocateShadowNode(const ShadowNode& shadowNode);
|
||||
|
||||
void destroyUnmountedShadowNode(const ShadowNodeFamily& family);
|
||||
|
||||
/*
|
||||
* Drains preallocatedViewsQueue_ by calling preallocateShadowView on each
|
||||
* item in the queue. Can be called by any thread.
|
||||
|
||||
@@ -282,6 +282,7 @@ void ShadowNode::cloneChildrenIfShared() {
|
||||
void ShadowNode::setMounted(bool mounted) const {
|
||||
if (mounted) {
|
||||
family_->setMostRecentState(getState());
|
||||
family_->setMounted();
|
||||
hasBeenMounted_ = mounted;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "ShadowNode.h"
|
||||
|
||||
#include <react/debug/react_native_assert.h>
|
||||
#include <react/featureflags/ReactNativeFeatureFlags.h>
|
||||
#include <react/renderer/core/ComponentDescriptor.h>
|
||||
#include <react/renderer/core/State.h>
|
||||
|
||||
@@ -58,10 +59,30 @@ ComponentName ShadowNodeFamily::getComponentName() const {
|
||||
return componentName_;
|
||||
}
|
||||
|
||||
void ShadowNodeFamily::setMounted() const {
|
||||
hasBeenMounted_ = true;
|
||||
}
|
||||
|
||||
const ComponentDescriptor& ShadowNodeFamily::getComponentDescriptor() const {
|
||||
return componentDescriptor_;
|
||||
}
|
||||
|
||||
void ShadowNodeFamily::onUnmountedFamilyDestroyed(
|
||||
std::function<void(const ShadowNodeFamily& family)> callback) const {
|
||||
onUnmountedFamilyDestroyedCallback_ = std::move(callback);
|
||||
}
|
||||
|
||||
Tag ShadowNodeFamily::getTag() const {
|
||||
return tag_;
|
||||
}
|
||||
|
||||
ShadowNodeFamily::~ShadowNodeFamily() {
|
||||
if (ReactNativeFeatureFlags::enableDeletionOfUnmountedViews() &&
|
||||
!hasBeenMounted_ && onUnmountedFamilyDestroyedCallback_ != nullptr) {
|
||||
onUnmountedFamilyDestroyedCallback_(*this);
|
||||
}
|
||||
}
|
||||
|
||||
AncestorList ShadowNodeFamily::getAncestors(
|
||||
const ShadowNode& ancestorShadowNode) const {
|
||||
auto families = std::vector<const ShadowNodeFamily*>{};
|
||||
|
||||
@@ -25,7 +25,7 @@ class State;
|
||||
* `ShadowNodeFamily` instances.
|
||||
*
|
||||
* Do not use this class as a general purpose container to share information
|
||||
* about a `ShadowNodeFamily`. Pelase define specific purpose containers in
|
||||
* about a `ShadowNodeFamily`. Please define specific purpose containers in
|
||||
* those cases.
|
||||
*
|
||||
*/
|
||||
@@ -87,12 +87,24 @@ class ShadowNodeFamily final {
|
||||
|
||||
SharedEventEmitter getEventEmitter() const;
|
||||
|
||||
/**
|
||||
* @param callback will be executed when an unmounted instance of
|
||||
* ShadowNodeFamily is destroyed.
|
||||
*/
|
||||
void onUnmountedFamilyDestroyed(
|
||||
std::function<void(const ShadowNodeFamily& family)> callback) const;
|
||||
|
||||
/*
|
||||
* Sets and gets the most recent state.
|
||||
*/
|
||||
std::shared_ptr<const State> getMostRecentState() const;
|
||||
void setMostRecentState(const std::shared_ptr<const State>& state) const;
|
||||
|
||||
/**
|
||||
* Mark this ShadowNodeFamily as mounted.
|
||||
*/
|
||||
void setMounted() const;
|
||||
|
||||
/*
|
||||
* Dispatches a state update with given priority.
|
||||
*/
|
||||
@@ -105,6 +117,17 @@ class ShadowNodeFamily final {
|
||||
*/
|
||||
mutable std::unique_ptr<folly::dynamic> nativeProps_DEPRECATED;
|
||||
|
||||
/**
|
||||
* @return tag for the ShadowNodeFamily.
|
||||
*/
|
||||
Tag getTag() const;
|
||||
|
||||
/**
|
||||
* Override destructor to call onUnmountedFamilyDestroyedCallback() for
|
||||
* ShadowViews that were preallocated but never mounted on the screen.
|
||||
*/
|
||||
~ShadowNodeFamily();
|
||||
|
||||
private:
|
||||
friend ShadowNode;
|
||||
friend State;
|
||||
@@ -121,6 +144,9 @@ class ShadowNodeFamily final {
|
||||
mutable std::shared_ptr<const State> mostRecentState_;
|
||||
mutable std::shared_mutex mutex_;
|
||||
|
||||
mutable std::function<void(ShadowNodeFamily& family)>
|
||||
onUnmountedFamilyDestroyedCallback_ = nullptr;
|
||||
|
||||
/*
|
||||
* Deprecated.
|
||||
*/
|
||||
@@ -165,6 +191,11 @@ class ShadowNodeFamily final {
|
||||
* For optimization purposes only.
|
||||
*/
|
||||
mutable bool hasParent_{false};
|
||||
|
||||
/*
|
||||
* Determines if the ShadowNodeFamily was ever mounted on the screen.
|
||||
*/
|
||||
mutable bool hasBeenMounted_{false};
|
||||
};
|
||||
|
||||
} // namespace facebook::react
|
||||
|
||||
Reference in New Issue
Block a user