Prototype View recycling for View

Summary:
Prototype of View Recycling for View + generic APIs.

Changelog: [Added][Android] Adding experimental View Recycling for Fabric on Android.

Reviewed By: mdvacca

Differential Revision: D36608419

fbshipit-source-id: c469ce2fe12ef9332d3def591118befc4a619870
This commit is contained in:
Joshua Gross authored and Facebook GitHub Bot committed 2022-05-26 03:40:21 -07:00
1 parent 0ef73f2a82
commit 7b778fbebb
9 files changed
+293 -11

No files matched your search

@@ -16,13 +16,16 @@ import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.common.mapbuffer.MapBuffer;
import com.facebook.react.config.ReactFeatureFlags;
import com.facebook.react.touch.JSResponderHandler;
import com.facebook.react.touch.ReactInterceptingViewGroup;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.uimanager.annotations.ReactPropGroup;
import com.facebook.react.uimanager.annotations.ReactPropertyHolder;
import com.facebook.yoga.YogaMeasureMode;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
/**
* Class responsible for knowing how to create and update catalyst Views of a given type. It is also
@@ -33,6 +36,30 @@ import java.util.Map;
public abstract class ViewManager<T extends View, C extends ReactShadowNode>
extends BaseJavaModule {
/**
* For View recycling: we store a Stack of unused, dead Views. This is null by default, and when
* null signals that View Recycling is disabled. `enableViewRecycling` must be explicitly called
* in a concrete constructor to enable View Recycling per ViewManager.
*/
private HashMap<Integer, Stack<T>> mRecyclableViews = null;
/** Call in constructor of concrete ViewManager class to enable. */
protected void enableViewRecycling() {
if (ReactFeatureFlags.enableViewRecycling) {
mRecyclableViews = new HashMap<>();
}
}
private @Nullable Stack<T> getRecyclableViewStack(int surfaceId) {
if (mRecyclableViews == null) {
return null;
}
if (!mRecyclableViews.containsKey(surfaceId)) {
mRecyclableViews.put(surfaceId, new Stack<>());
}
return mRecyclableViews.get(surfaceId);
}
/**
* For the vast majority of ViewManagers, you will not need to override this. Only override this
* if you really know what you're doing and have a very unique use-case.
@@ -137,7 +164,13 @@ public abstract class ViewManager<T extends View, C extends ReactShadowNode>
@NonNull ThemedReactContext reactContext,
@Nullable ReactStylesDiffMap initialProps,
@Nullable StateWrapper stateWrapper) {
T view = createViewInstance(reactContext);
T view = null;
@Nullable Stack<T> recyclableViews = getRecyclableViewStack(reactContext.getSurfaceId());
if (recyclableViews != null && !recyclableViews.empty()) {
view = recycleView(reactContext, recyclableViews.pop());
} else {
view = createViewInstance(reactContext);
}
view.setId(reactTag);
addEventEmitters(reactContext, view);
if (initialProps != null) {
@@ -157,7 +190,27 @@ public abstract class ViewManager<T extends View, C extends ReactShadowNode>
* Called when view is detached from view hierarchy and allows for some additional cleanup by the
* {@link ViewManager} subclass.
*/
public void onDropViewInstance(@NonNull T view) {}
public void onDropViewInstance(@NonNull T view) {
@Nullable
Stack<T> recyclableViews =
getRecyclableViewStack(((ThemedReactContext) view.getContext()).getSurfaceId());
// By default we treat views as recyclable
if (recyclableViews != null) {
recyclableViews.push(prepareToRecycleView((ThemedReactContext) view.getContext(), view));
}
}
/**
* Called when a View is removed from the hierachy. This should be used to reset any properties.
*/
protected T prepareToRecycleView(@NonNull ThemedReactContext reactContext, @NonNull T view) {
return view;
}
/** Called when a View is going to be reused. */
protected T recycleView(@NonNull ThemedReactContext reactContext, @NonNull T view) {
return view;
}
/**
* Subclasses can override this method to install custom event emitters on the given View. You
@@ -370,4 +423,24 @@ public abstract class ViewManager<T extends View, C extends ReactShadowNode>
* components support setting padding, the default implementation of this method does nothing.
*/
public void setPadding(T view, int left, int top, int right, int bottom) {}
/**
* Lifecycle method: called when a surface is stopped. Currently only used for View Recycling
* cleanup. There is no corresponding startSurface lifecycle event for ViewManagers because we
* currently only need this for recycling cleanup. Only called in Fabric.
*/
public void onSurfaceStopped(int surfaceId) {
if (mRecyclableViews != null) {
mRecyclableViews.remove(surfaceId);
}
}
/** With even slight memory pressure, we immediately evict all recyclable Views. */
/* package */ void trimMemory() {
// Wipe out all existing recyclable Views, but do not disable View Recycling entirely.
// We only take any action if View Recycling is already enabled.
if (mRecyclableViews != null) {
mRecyclableViews = new HashMap<>();
}
}
}