Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
import com.facebook.react.uimanager.ViewManager;
import com.facebook.react.uimanager.ViewManagerRegistry;
import com.facebook.yoga.YogaMeasureMode;
import java.util.LinkedHashMap;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;

/**
Expand All @@ -52,13 +54,15 @@ public class MountingManager {
public static final String TAG = MountingManager.class.getSimpleName();
private static final boolean SHOW_CHANGED_VIEW_HIERARCHIES = ReactBuildConfig.DEBUG && false;

@NonNull private final ConcurrentHashMap<Integer, ViewState> mTagToViewState;
@NonNull private final ConcurrentHashMap<Integer, ViewState> mTagToViewState; // any thread
@NonNull private final LinkedHashMap<Integer, TreeSet<Integer>> mRootTagToTags; // UI thread only
@NonNull private final JSResponderHandler mJSResponderHandler = new JSResponderHandler();
@NonNull private final ViewManagerRegistry mViewManagerRegistry;
@NonNull private final RootViewManager mRootViewManager = new RootViewManager();

public MountingManager(@NonNull ViewManagerRegistry viewManagerRegistry) {
mTagToViewState = new ConcurrentHashMap<>();
mRootTagToTags = new LinkedHashMap<>();
mViewManagerRegistry = viewManagerRegistry;
}

Expand Down Expand Up @@ -120,6 +124,7 @@ public void run() {
+ "explicitly overwrite the id field to View.NO_ID before calling addRootView.");
}
rootView.setId(reactRootTag);
mRootTagToTags.put(reactRootTag, new TreeSet<Integer>());
}
});
}
Expand All @@ -131,6 +136,17 @@ public void deleteRootView(int reactRootTag) {
if (rootViewState != null && rootViewState.mView != null) {
dropView(rootViewState.mView, true);
}

// Iterate through all tags of reactRootTag, delete all retained Views associated with tags
// Tags that could be left-over, even after `dropView` is called: PreAllocated Views that were
// never properly deleted, for example. There might be other causes of leaks as well.
// This doesn't remove Views from the View Hierarchy, it just ensures that we don't leak
// memory by holding onto native Views.
TreeSet<Integer> tags = mRootTagToTags.get(reactRootTag);
mRootTagToTags.remove(reactRootTag);
for (int tag : tags) {
mTagToViewState.remove(tag);
}
}

/** Releases all references to given native View. */
Expand Down Expand Up @@ -512,6 +528,7 @@ public void run() {
public void createView(
@NonNull ThemedReactContext themedReactContext,
@NonNull String componentName,
int rootTag,
int reactTag,
@Nullable ReadableMap props,
@Nullable StateWrapper stateWrapper,
Expand Down Expand Up @@ -542,6 +559,7 @@ public void createView(
viewState.mCurrentState = (stateWrapper != null ? stateWrapper.getState() : null);

mTagToViewState.put(reactTag, viewState);
mRootTagToTags.get(rootTag).add(reactTag);
}

@UiThread
Expand Down Expand Up @@ -622,7 +640,7 @@ public void updatePadding(int reactTag, int left, int top, int right, int bottom
}

@UiThread
public void deleteView(int reactTag) {
public void deleteView(int rootTag, int reactTag) {
UiThreadUtil.assertOnUiThread();
ViewState viewState = getNullableViewState(reactTag);

Expand All @@ -643,6 +661,7 @@ public void deleteView(int reactTag) {
// Additionally, as documented in `dropView`, we cannot always trust a
// view's children to be up-to-date.
mTagToViewState.remove(reactTag);
mRootTagToTags.get(rootTag).remove(reactTag);

// For non-root views we notify viewmanager with {@link ViewManager#onDropInstance}
ViewManager viewManager = viewState.mViewManager;
Expand Down Expand Up @@ -675,6 +694,7 @@ public void updateState(final int reactTag, @Nullable StateWrapper stateWrapper)
public void preallocateView(
@NonNull ThemedReactContext reactContext,
String componentName,
int rootTag,
int reactTag,
@Nullable ReadableMap props,
@Nullable StateWrapper stateWrapper,
Expand All @@ -685,7 +705,12 @@ public void preallocateView(
"View for component " + componentName + " with tag " + reactTag + " already exists.");
}

createView(reactContext, componentName, reactTag, props, stateWrapper, isLayoutable);
// Views can be preallocated before the surface is started
if (mRootTagToTags.get(rootTag) == null) {
mRootTagToTags.put(rootTag, new TreeSet<Integer>());
}

createView(reactContext, componentName, rootTag, reactTag, props, stateWrapper, isLayoutable);
}

@UiThread
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,12 +140,13 @@ public void execute(@NonNull MountingManager mountingManager) {
mountingManager.createView(
mContext,
componentName,
mRootTag,
mIntBuffer[i++],
castToProps(mObjBuffer[j++]),
castToState(mObjBuffer[j++]),
mIntBuffer[i++] == 1);
} else if (type == INSTRUCTION_DELETE) {
mountingManager.deleteView(mIntBuffer[i++]);
mountingManager.deleteView(mRootTag, mIntBuffer[i++]);
} else if (type == INSTRUCTION_INSERT) {
int tag = mIntBuffer[i++];
int parentTag = mIntBuffer[i++];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public void execute(@NonNull MountingManager mountingManager) {
+ mRootTag);
}
mountingManager.preallocateView(
mContext, mComponent, mReactTag, mProps, mStateWrapper, mIsLayoutable);
mContext, mComponent, mRootTag, mReactTag, mProps, mStateWrapper, mIsLayoutable);
}

@Override
Expand Down