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:
Andy Street 2016-03-31 09:20:19 -07:00 committed by Facebook Github Bot 0
parent c3c7249689
commit 6a3b334139
1 changed files with 63 additions and 10 deletions

View File

@ -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();
}
}
}
}