From eef03fd5524887da19eb413658c021f9c806af1d Mon Sep 17 00:00:00 2001 From: Andy Street Date: Thu, 9 Jun 2016 08:40:50 -0700 Subject: [PATCH] Fix bug where UIOperations aren't executed after hosting Activity is paused. Summary: When we hit the back button and unmount the ReactRootView, we tell JS to unmount the application root node, which causes JS to asynchronously come back and tell the UIManager to drop the corresponding root view. This issue was that by the time JS gets back to us, we likely will have already paused the UIManager frame callback which means the view unmounting never actually happens: it just gets stuck in the queue. The solution is to immediately execute batches when they are enqueued when the frame callback isn't running. Reviewed By: lexs Differential Revision: D3398958 fbshipit-source-id: 0de81061a97a119be4cb0b12d6f01c1cec8e8171 --- .../react/uimanager/UIViewOperationQueue.java | 35 ++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIViewOperationQueue.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIViewOperationQueue.java index a5399a2e2..b10c2e6ce 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIViewOperationQueue.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIViewOperationQueue.java @@ -521,8 +521,8 @@ public class UIViewOperationQueue { private ArrayList mOperations = new ArrayList<>(); @GuardedBy("mNonBatchedOperationsLock") private ArrayDeque mNonBatchedOperations = new ArrayDeque<>(); - private @Nullable NotThreadSafeViewHierarchyUpdateDebugListener mViewHierarchyUpdateDebugListener; + private boolean mIsDispatchUIFrameCallbackEnqueued = false; public UIViewOperationQueue( ReactApplicationContext reactContext, @@ -779,17 +779,41 @@ public class UIViewOperationQueue { } }); } + + // In the case where the frame callback isn't enqueued, the UI isn't being displayed or is being + // destroyed. In this case it's no longer important to align to frames, but it is imporant to make + // sure any late-arriving UI commands are executed. + if (!mIsDispatchUIFrameCallbackEnqueued) { + UiThreadUtil.runOnUiThread( + new Runnable() { + @Override + public void run() { + flushPendingBatches(); + } + }); + } } /* package */ void resumeFrameCallback() { + mIsDispatchUIFrameCallbackEnqueued = true; ReactChoreographer.getInstance() .postFrameCallback(ReactChoreographer.CallbackType.DISPATCH_UI, mDispatchUIFrameCallback); } /* package */ void pauseFrameCallback() { - + mIsDispatchUIFrameCallbackEnqueued = false; ReactChoreographer.getInstance() .removeFrameCallback(ReactChoreographer.CallbackType.DISPATCH_UI, mDispatchUIFrameCallback); + flushPendingBatches(); + } + + private void flushPendingBatches() { + synchronized (mDispatchRunnablesLock) { + for (int i = 0; i < mDispatchUIRunnables.size(); i++) { + mDispatchUIRunnables.get(i).run(); + } + mDispatchUIRunnables.clear(); + } } /** @@ -825,12 +849,7 @@ public class UIViewOperationQueue { Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE); } - synchronized (mDispatchRunnablesLock) { - for (int i = 0; i < mDispatchUIRunnables.size(); i++) { - mDispatchUIRunnables.get(i).run(); - } - mDispatchUIRunnables.clear(); - } + flushPendingBatches(); ReactChoreographer.getInstance().postFrameCallback( ReactChoreographer.CallbackType.DISPATCH_UI, this);