Start creating views before end of batch
Summary:Creating views shouldn't have side effects so we can start doing them before the end of a batch. In order to limit the effect on framerate, we stop executing these create view operations after we've passed halfway through the frame. Note, this doesn't seem to work yet for nodes: I'll address that in a followup diff. Reviewed By: lexs Differential Revision: D3120631 fb-gh-sync-id: 981540fac5a7499158146adb72c1be21fd0b5702 fbshipit-source-id: 981540fac5a7499158146adb72c1be21fd0b5702
This commit is contained in:
parent
c3c7249689
commit
6a3b334139
|
@ -12,6 +12,7 @@ package com.facebook.react.uimanager;
|
|||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.GuardedBy;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
@ -417,8 +418,6 @@ public class UIViewOperationQueue {
|
|||
}
|
||||
}
|
||||
|
||||
private ArrayList<UIOperation> mOperations = new ArrayList<>();
|
||||
|
||||
private final class FindTargetForTouchOperation implements UIOperation {
|
||||
|
||||
private final int mReactTag;
|
||||
|
@ -493,14 +492,17 @@ public class UIViewOperationQueue {
|
|||
|
||||
private final NativeViewHierarchyManager mNativeViewHierarchyManager;
|
||||
private final AnimationRegistry mAnimationRegistry;
|
||||
|
||||
private final Object mDispatchRunnablesLock = new Object();
|
||||
private final Object mNonBatchedOperationsLock = new Object();
|
||||
private final DispatchUIFrameCallback mDispatchUIFrameCallback;
|
||||
private final ReactApplicationContext mReactApplicationContext;
|
||||
|
||||
@GuardedBy("mDispatchRunnablesLock")
|
||||
private final ArrayList<Runnable> mDispatchUIRunnables = new ArrayList<>();
|
||||
|
||||
private ArrayList<UIOperation> mOperations = new ArrayList<>();
|
||||
@GuardedBy("mNonBatchedOperationsLock")
|
||||
private ArrayDeque<UIOperation> mNonBatchedOperations = new ArrayDeque<>();
|
||||
|
||||
private @Nullable NotThreadSafeViewHierarchyUpdateDebugListener mViewHierarchyUpdateDebugListener;
|
||||
|
||||
public UIViewOperationQueue(
|
||||
|
@ -604,12 +606,14 @@ public class UIViewOperationQueue {
|
|||
int viewReactTag,
|
||||
String viewClassName,
|
||||
@Nullable ReactStylesDiffMap initialProps) {
|
||||
mOperations.add(
|
||||
synchronized (mNonBatchedOperationsLock) {
|
||||
mNonBatchedOperations.addLast(
|
||||
new CreateViewOperation(
|
||||
themedContext,
|
||||
viewReactTag,
|
||||
viewClassName,
|
||||
initialProps));
|
||||
themedContext,
|
||||
viewReactTag,
|
||||
viewClassName,
|
||||
initialProps));
|
||||
}
|
||||
}
|
||||
|
||||
public void enqueueUpdateProperties(int reactTag, String className, ReactStylesDiffMap props) {
|
||||
|
@ -699,6 +703,17 @@ public class UIViewOperationQueue {
|
|||
mOperations = new ArrayList<>();
|
||||
}
|
||||
|
||||
final UIOperation[] nonBatchedOperations;
|
||||
synchronized (mNonBatchedOperationsLock) {
|
||||
if (!mNonBatchedOperations.isEmpty()) {
|
||||
nonBatchedOperations =
|
||||
mNonBatchedOperations.toArray(new UIOperation[mNonBatchedOperations.size()]);
|
||||
mNonBatchedOperations.clear();
|
||||
} else {
|
||||
nonBatchedOperations = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (mViewHierarchyUpdateDebugListener != null) {
|
||||
mViewHierarchyUpdateDebugListener.onViewHierarchyUpdateEnqueued();
|
||||
}
|
||||
|
@ -712,6 +727,14 @@ public class UIViewOperationQueue {
|
|||
.arg("BatchId", batchId)
|
||||
.flush();
|
||||
try {
|
||||
// All nonBatchedOperations should be executed before regular operations as
|
||||
// regular operations may depend on them
|
||||
if (nonBatchedOperations != null) {
|
||||
for (UIOperation op : nonBatchedOperations) {
|
||||
op.execute();
|
||||
}
|
||||
}
|
||||
|
||||
if (operations != null) {
|
||||
for (int i = 0; i < operations.size(); i++) {
|
||||
operations.get(i).execute();
|
||||
|
@ -760,12 +783,22 @@ public class UIViewOperationQueue {
|
|||
*/
|
||||
private class DispatchUIFrameCallback extends GuardedChoreographerFrameCallback {
|
||||
|
||||
private static final int MIN_TIME_LEFT_IN_FRAME_TO_SCHEDULE_MORE_WORK_MS = 8;
|
||||
private static final int FRAME_TIME_MS = 16;
|
||||
|
||||
private DispatchUIFrameCallback(ReactContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFrameGuarded(long frameTimeNanos) {
|
||||
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "dispatchNonBatchedUIOperations");
|
||||
try {
|
||||
dispatchPendingNonBatchedOperations(frameTimeNanos);
|
||||
} finally {
|
||||
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
|
||||
}
|
||||
|
||||
synchronized (mDispatchRunnablesLock) {
|
||||
for (int i = 0; i < mDispatchUIRunnables.size(); i++) {
|
||||
mDispatchUIRunnables.get(i).run();
|
||||
|
@ -774,7 +807,27 @@ public class UIViewOperationQueue {
|
|||
}
|
||||
|
||||
ReactChoreographer.getInstance().postFrameCallback(
|
||||
ReactChoreographer.CallbackType.DISPATCH_UI, this);
|
||||
ReactChoreographer.CallbackType.DISPATCH_UI, this);
|
||||
}
|
||||
|
||||
private void dispatchPendingNonBatchedOperations(long frameTimeNanos) {
|
||||
while (true) {
|
||||
long timeLeftInFrame = FRAME_TIME_MS - ((System.nanoTime() - frameTimeNanos) / 1000000);
|
||||
if (timeLeftInFrame < MIN_TIME_LEFT_IN_FRAME_TO_SCHEDULE_MORE_WORK_MS) {
|
||||
break;
|
||||
}
|
||||
|
||||
UIOperation nextOperation;
|
||||
synchronized (mNonBatchedOperationsLock) {
|
||||
if (mNonBatchedOperations.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
nextOperation = mNonBatchedOperations.pollFirst();
|
||||
}
|
||||
|
||||
nextOperation.execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue