Maintain a separate queue for ViewCommands and execute before everything else

Summary:
ViewCommands are sort of like setNativeProps updates, in that they're direct manipulations of Views on-screen that don't go through the normal
commit/diff process that other UI updates do. This is an MVP that shows how we can do this in non-Fabric RN.

Changelog: [Internal] experiment, allow ViewCommands to be executed before other types of UIOperations

Reviewed By: axe-fb, mdvacca

Differential Revision: D20378633

fbshipit-source-id: 5f3c54d3c84b4e4f7cb060a9505b20b0e5b7afed
This commit is contained in:
Joshua Gross
2020-03-10 20:26:44 -07:00
committed by Facebook Github Bot
parent 99ceb6afba
commit 8b61578076
2 changed files with 41 additions and 3 deletions
@@ -79,6 +79,12 @@ public class ReactFeatureFlags {
*/
public static boolean allowDisablingImmediateExecutionOfScheduleMountItems = false;
/**
* Temporary flag. See UIImplementation: if this flag is enabled, ViewCommands will be queued and
* executed before any other types of UI operations.
*/
public static boolean allowEarlyViewCommandExecution = false;
/**
* This react flag enables a custom algorithm for the getChildVisibleRect() method in the classes
* ReactViewGroup, ReactHorizontalScrollView and ReactScrollView.
@@ -21,6 +21,7 @@ import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.SoftAssertions;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.common.ReactConstants;
import com.facebook.react.config.ReactFeatureFlags;
import com.facebook.react.modules.core.ReactChoreographer;
import com.facebook.react.uimanager.debug.NotThreadSafeViewHierarchyUpdateDebugListener;
import com.facebook.systrace.Systrace;
@@ -518,6 +519,9 @@ public class UIViewOperationQueue {
private final DispatchUIFrameCallback mDispatchUIFrameCallback;
private final ReactApplicationContext mReactApplicationContext;
private final boolean mAllowViewCommandsQueue;
private ArrayList<UIOperation> mViewCommandOperations = new ArrayList<>();
// Only called from the UIManager queue?
private ArrayList<UIOperation> mOperations = new ArrayList<>();
@@ -556,6 +560,7 @@ public class UIViewOperationQueue {
? DEFAULT_MIN_TIME_LEFT_IN_FRAME_FOR_NONBATCHED_OPERATION_MS
: minTimeLeftInFrameForNonBatchedOperationMs);
mReactApplicationContext = reactContext;
mAllowViewCommandsQueue = ReactFeatureFlags.allowEarlyViewCommandExecution;
}
/*package*/ NativeViewHierarchyManager getNativeViewHierarchyManager() {
@@ -591,7 +596,7 @@ public class UIViewOperationQueue {
}
public boolean isEmpty() {
return mOperations.isEmpty();
return mOperations.isEmpty() && mViewCommandOperations.isEmpty();
}
public void addRootView(final int tag, final View rootView) {
@@ -625,12 +630,24 @@ public class UIViewOperationQueue {
@Deprecated
public void enqueueDispatchCommand(
int reactTag, int commandId, @Nullable ReadableArray commandArgs) {
mOperations.add(new DispatchCommandOperation(reactTag, commandId, commandArgs));
final DispatchCommandOperation command =
new DispatchCommandOperation(reactTag, commandId, commandArgs);
if (mAllowViewCommandsQueue) {
mViewCommandOperations.add(command);
} else {
mOperations.add(command);
}
}
public void enqueueDispatchCommand(
int reactTag, String commandId, @Nullable ReadableArray commandArgs) {
mOperations.add(new DispatchStringCommandOperation(reactTag, commandId, commandArgs));
final DispatchStringCommandOperation command =
new DispatchStringCommandOperation(reactTag, commandId, commandArgs);
if (mAllowViewCommandsQueue) {
mViewCommandOperations.add(command);
} else {
mOperations.add(command);
}
}
public void enqueueUpdateExtraData(int reactTag, Object extraData) {
@@ -742,6 +759,14 @@ public class UIViewOperationQueue {
// Store the current operation queues to dispatch and create new empty ones to continue
// receiving new operations
final ArrayList<UIOperation> viewCommandOperations;
if (!mViewCommandOperations.isEmpty()) {
viewCommandOperations = mViewCommandOperations;
mViewCommandOperations = new ArrayList<>();
} else {
viewCommandOperations = null;
}
final ArrayList<UIOperation> batchedOperations;
if (!mOperations.isEmpty()) {
batchedOperations = mOperations;
@@ -774,6 +799,13 @@ public class UIViewOperationQueue {
try {
long runStartTime = SystemClock.uptimeMillis();
// All ViewCommands should be executed first as a perf optimization
if (mViewCommandOperations != null) {
for (UIOperation viewCommandOp : mViewCommandOperations) {
viewCommandOp.execute();
}
}
// All nonBatchedOperations should be executed before regular operations as
// regular operations may depend on them
if (nonBatchedOperations != null) {