Skip to content

Commit

Permalink
Maintain a separate queue for ViewCommands and execute before everyth…
Browse files Browse the repository at this point in the history
…ing 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
  • Loading branch information
JoshuaGross authored and facebook-github-bot committed Mar 11, 2020
1 parent 99ceb6a commit 8b61578
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
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;
Expand Down Expand Up @@ -518,6 +519,9 @@ public void execute() {
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<>();

Expand Down Expand Up @@ -556,6 +560,7 @@ public UIViewOperationQueue(
? DEFAULT_MIN_TIME_LEFT_IN_FRAME_FOR_NONBATCHED_OPERATION_MS
: minTimeLeftInFrameForNonBatchedOperationMs);
mReactApplicationContext = reactContext;
mAllowViewCommandsQueue = ReactFeatureFlags.allowEarlyViewCommandExecution;
}

/*package*/ NativeViewHierarchyManager getNativeViewHierarchyManager() {
Expand Down Expand Up @@ -591,7 +596,7 @@ public Map<String, Long> getProfiledBatchPerfCounters() {
}

public boolean isEmpty() {
return mOperations.isEmpty();
return mOperations.isEmpty() && mViewCommandOperations.isEmpty();
}

public void addRootView(final int tag, final View rootView) {
Expand Down Expand Up @@ -625,12 +630,24 @@ public void enqueueClearJSResponder() {
@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) {
Expand Down Expand Up @@ -742,6 +759,14 @@ public void dispatchViewUpdates(

// 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;
Expand Down Expand Up @@ -774,6 +799,13 @@ public void run() {
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) {
Expand Down

0 comments on commit 8b61578

Please sign in to comment.