From aeda31428df7b88c684f15edd9687ed6ed8cfc21 Mon Sep 17 00:00:00 2001 From: Denis Koroskin Date: Wed, 25 Nov 2015 21:17:13 -0800 Subject: [PATCH] When addRootView() is called in a background thread, execute it as a generic UIOperation command in UIViewOperationQueue Summary: public There is really no reason NativeViewHierarchyManager.addRootView() should be performed synchroniously when called from background thread, as long as it is executed before every other command in UIViewOperationQueue, and we can ensure that by putting add view command at the front of the queue. When that happpens, the queue should always be empty anyway, but it's best to be safe. This eliminates an unnecessary blocking call and should overall make the code simpler and safer (Semaphores can timeout). Reviewed By: astreet Differential Revision: D2462680 fb-gh-sync-id: 784ac6573a455019b93628c70992f3830b9d6f1f --- .../react/uimanager/UIManagerModule.java | 30 ++--------------- .../react/uimanager/UIViewOperationQueue.java | 32 +++++++++++++++++++ 2 files changed, 35 insertions(+), 27 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java index f8609dc61..5633c32b9 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java @@ -14,8 +14,6 @@ import javax.annotation.Nullable; import java.util.Arrays; import java.util.List; import java.util.Map; -import java.util.concurrent.Semaphore; -import java.util.concurrent.TimeUnit; import android.util.DisplayMetrics; @@ -31,8 +29,6 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.bridge.SoftAssertions; -import com.facebook.react.bridge.UiThreadUtil; import com.facebook.react.bridge.WritableArray; import com.facebook.react.uimanager.debug.NotThreadSafeViewHierarchyUpdateDebugListener; import com.facebook.react.uimanager.events.EventDispatcher; @@ -75,7 +71,6 @@ public class UIManagerModule extends ReactContextBaseJavaModule implements // increment here is 10 private static final int ROOT_VIEW_TAG_INCREMENT = 10; - private final NativeViewHierarchyManager mNativeViewHierarchyManager; private final EventDispatcher mEventDispatcher; private final ShadowNodeRegistry mShadowNodeRegistry = new ShadowNodeRegistry(); private final ViewManagerRegistry mViewManagers; @@ -92,10 +87,9 @@ public class UIManagerModule extends ReactContextBaseJavaModule implements super(reactContext); mViewManagers = new ViewManagerRegistry(viewManagerList); mEventDispatcher = new EventDispatcher(reactContext); - mNativeViewHierarchyManager = new NativeViewHierarchyManager(mViewManagers); mOperationsQueue = new UIViewOperationQueue( reactContext, - mNativeViewHierarchyManager); + new NativeViewHierarchyManager(mViewManagers)); mNativeViewHierarchyOptimizer = new NativeViewHierarchyOptimizer( mOperationsQueue, mShadowNodeRegistry); @@ -197,26 +191,8 @@ public class UIManagerModule extends ReactContextBaseJavaModule implements mShadowNodeRegistry.addRootNode(rootCSSNode); - if (UiThreadUtil.isOnUiThread()) { - mNativeViewHierarchyManager.addRootView(tag, rootView, themedRootContext); - } else { - final Semaphore semaphore = new Semaphore(0); - getReactApplicationContext().runOnUiQueueThread( - new Runnable() { - @Override - public void run() { - mNativeViewHierarchyManager.addRootView(tag, rootView, themedRootContext); - semaphore.release(); - } - }); - try { - SoftAssertions.assertCondition( - semaphore.tryAcquire(5000, TimeUnit.MILLISECONDS), - "Timed out adding root view"); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } + // register it within NativeViewHierarchyManager + mOperationsQueue.addRootView(tag, rootView, themedRootContext); return tag; } 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 d118561be..21f1128d8 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIViewOperationQueue.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIViewOperationQueue.java @@ -13,13 +13,17 @@ import javax.annotation.Nullable; import javax.annotation.concurrent.GuardedBy; import java.util.ArrayList; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; import com.facebook.react.animation.Animation; import com.facebook.react.animation.AnimationRegistry; import com.facebook.react.bridge.Callback; import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.SoftAssertions; import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.bridge.UiThreadUtil; import com.facebook.react.uimanager.debug.NotThreadSafeViewHierarchyUpdateDebugListener; import com.facebook.systrace.Systrace; import com.facebook.systrace.SystraceMessage; @@ -448,6 +452,7 @@ public class UIViewOperationQueue { private final Object mDispatchRunnablesLock = new Object(); private final DispatchUIFrameCallback mDispatchUIFrameCallback; + private final ReactApplicationContext mReactApplicationContext; @GuardedBy("mDispatchRunnablesLock") private final ArrayList mDispatchUIRunnables = new ArrayList<>(); @@ -460,6 +465,7 @@ public class UIViewOperationQueue { mNativeViewHierarchyManager = nativeViewHierarchyManager; mAnimationRegistry = nativeViewHierarchyManager.getAnimationRegistry(); mDispatchUIFrameCallback = new DispatchUIFrameCallback(reactContext); + mReactApplicationContext = reactContext; } public void setViewHierarchyUpdateDebugListener( @@ -471,6 +477,32 @@ public class UIViewOperationQueue { return mOperations.isEmpty(); } + public void addRootView( + final int tag, + final SizeMonitoringFrameLayout rootView, + final ThemedReactContext themedRootContext) { + if (UiThreadUtil.isOnUiThread()) { + mNativeViewHierarchyManager.addRootView(tag, rootView, themedRootContext); + } else { + final Semaphore semaphore = new Semaphore(0); + mReactApplicationContext.runOnUiQueueThread( + new Runnable() { + @Override + public void run() { + mNativeViewHierarchyManager.addRootView(tag, rootView, themedRootContext); + semaphore.release(); + } + }); + try { + SoftAssertions.assertCondition( + semaphore.tryAcquire(5000, TimeUnit.MILLISECONDS), + "Timed out adding root view"); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } + public void enqueueRemoveRootView(int rootViewTag) { mOperations.add(new RemoveRootViewOperation(rootViewTag)); }