Skip to content

Commit 6a3b334

Browse files
astreetFacebook Github Bot 0
authored andcommitted
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
1 parent c3c7249 commit 6a3b334

1 file changed

Lines changed: 63 additions & 10 deletions

File tree

ReactAndroid/src/main/java/com/facebook/react/uimanager/UIViewOperationQueue.java

Lines changed: 63 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import javax.annotation.Nullable;
1313
import javax.annotation.concurrent.GuardedBy;
1414

15+
import java.util.ArrayDeque;
1516
import java.util.ArrayList;
1617
import java.util.concurrent.Semaphore;
1718
import java.util.concurrent.TimeUnit;
@@ -417,8 +418,6 @@ public void execute() {
417418
}
418419
}
419420

420-
private ArrayList<UIOperation> mOperations = new ArrayList<>();
421-
422421
private final class FindTargetForTouchOperation implements UIOperation {
423422

424423
private final int mReactTag;
@@ -493,14 +492,17 @@ public void execute() {
493492

494493
private final NativeViewHierarchyManager mNativeViewHierarchyManager;
495494
private final AnimationRegistry mAnimationRegistry;
496-
497495
private final Object mDispatchRunnablesLock = new Object();
496+
private final Object mNonBatchedOperationsLock = new Object();
498497
private final DispatchUIFrameCallback mDispatchUIFrameCallback;
499498
private final ReactApplicationContext mReactApplicationContext;
500-
501499
@GuardedBy("mDispatchRunnablesLock")
502500
private final ArrayList<Runnable> mDispatchUIRunnables = new ArrayList<>();
503501

502+
private ArrayList<UIOperation> mOperations = new ArrayList<>();
503+
@GuardedBy("mNonBatchedOperationsLock")
504+
private ArrayDeque<UIOperation> mNonBatchedOperations = new ArrayDeque<>();
505+
504506
private @Nullable NotThreadSafeViewHierarchyUpdateDebugListener mViewHierarchyUpdateDebugListener;
505507

506508
public UIViewOperationQueue(
@@ -604,12 +606,14 @@ public void enqueueCreateView(
604606
int viewReactTag,
605607
String viewClassName,
606608
@Nullable ReactStylesDiffMap initialProps) {
607-
mOperations.add(
609+
synchronized (mNonBatchedOperationsLock) {
610+
mNonBatchedOperations.addLast(
608611
new CreateViewOperation(
609-
themedContext,
610-
viewReactTag,
611-
viewClassName,
612-
initialProps));
612+
themedContext,
613+
viewReactTag,
614+
viewClassName,
615+
initialProps));
616+
}
613617
}
614618

615619
public void enqueueUpdateProperties(int reactTag, String className, ReactStylesDiffMap props) {
@@ -699,6 +703,17 @@ public void enqueueSendAccessibilityEvent(int tag, int eventType) {
699703
mOperations = new ArrayList<>();
700704
}
701705

706+
final UIOperation[] nonBatchedOperations;
707+
synchronized (mNonBatchedOperationsLock) {
708+
if (!mNonBatchedOperations.isEmpty()) {
709+
nonBatchedOperations =
710+
mNonBatchedOperations.toArray(new UIOperation[mNonBatchedOperations.size()]);
711+
mNonBatchedOperations.clear();
712+
} else {
713+
nonBatchedOperations = null;
714+
}
715+
}
716+
702717
if (mViewHierarchyUpdateDebugListener != null) {
703718
mViewHierarchyUpdateDebugListener.onViewHierarchyUpdateEnqueued();
704719
}
@@ -712,6 +727,14 @@ public void run() {
712727
.arg("BatchId", batchId)
713728
.flush();
714729
try {
730+
// All nonBatchedOperations should be executed before regular operations as
731+
// regular operations may depend on them
732+
if (nonBatchedOperations != null) {
733+
for (UIOperation op : nonBatchedOperations) {
734+
op.execute();
735+
}
736+
}
737+
715738
if (operations != null) {
716739
for (int i = 0; i < operations.size(); i++) {
717740
operations.get(i).execute();
@@ -760,12 +783,22 @@ public void run() {
760783
*/
761784
private class DispatchUIFrameCallback extends GuardedChoreographerFrameCallback {
762785

786+
private static final int MIN_TIME_LEFT_IN_FRAME_TO_SCHEDULE_MORE_WORK_MS = 8;
787+
private static final int FRAME_TIME_MS = 16;
788+
763789
private DispatchUIFrameCallback(ReactContext reactContext) {
764790
super(reactContext);
765791
}
766792

767793
@Override
768794
public void doFrameGuarded(long frameTimeNanos) {
795+
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "dispatchNonBatchedUIOperations");
796+
try {
797+
dispatchPendingNonBatchedOperations(frameTimeNanos);
798+
} finally {
799+
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
800+
}
801+
769802
synchronized (mDispatchRunnablesLock) {
770803
for (int i = 0; i < mDispatchUIRunnables.size(); i++) {
771804
mDispatchUIRunnables.get(i).run();
@@ -774,7 +807,27 @@ public void doFrameGuarded(long frameTimeNanos) {
774807
}
775808

776809
ReactChoreographer.getInstance().postFrameCallback(
777-
ReactChoreographer.CallbackType.DISPATCH_UI, this);
810+
ReactChoreographer.CallbackType.DISPATCH_UI, this);
811+
}
812+
813+
private void dispatchPendingNonBatchedOperations(long frameTimeNanos) {
814+
while (true) {
815+
long timeLeftInFrame = FRAME_TIME_MS - ((System.nanoTime() - frameTimeNanos) / 1000000);
816+
if (timeLeftInFrame < MIN_TIME_LEFT_IN_FRAME_TO_SCHEDULE_MORE_WORK_MS) {
817+
break;
818+
}
819+
820+
UIOperation nextOperation;
821+
synchronized (mNonBatchedOperationsLock) {
822+
if (mNonBatchedOperations.isEmpty()) {
823+
break;
824+
}
825+
826+
nextOperation = mNonBatchedOperations.pollFirst();
827+
}
828+
829+
nextOperation.execute();
830+
}
778831
}
779832
}
780833
}

0 commit comments

Comments
 (0)