1212import javax .annotation .Nullable ;
1313import javax .annotation .concurrent .GuardedBy ;
1414
15+ import java .util .ArrayDeque ;
1516import java .util .ArrayList ;
1617import java .util .concurrent .Semaphore ;
1718import 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