allow calling NativeViewHierarchyManager.addRootView() off the UI thread

Reviewed By: yungsters, shergin

Differential Revision: D4998706

fbshipit-source-id: f95cdcb0d193ffff2dfe39f9523a222d958ba5c6
This commit is contained in:
Aaron Chiu 2017-05-07 13:45:22 -07:00 committed by Facebook Github Bot
parent 7932b93fef
commit 20c2ae85f0
2 changed files with 32 additions and 46 deletions

View File

@ -91,7 +91,7 @@ public class NativeViewHierarchyManager {
mRootViewManager = manager; mRootViewManager = manager;
} }
public final View resolveView(int tag) { public synchronized final View resolveView(int tag) {
View view = mTagsToViews.get(tag); View view = mTagsToViews.get(tag);
if (view == null) { if (view == null) {
throw new IllegalViewOperationException("Trying to resolve view with tag " + tag throw new IllegalViewOperationException("Trying to resolve view with tag " + tag
@ -100,7 +100,7 @@ public class NativeViewHierarchyManager {
return view; return view;
} }
public final ViewManager resolveViewManager(int tag) { public synchronized final ViewManager resolveViewManager(int tag) {
ViewManager viewManager = mTagsToViewManagers.get(tag); ViewManager viewManager = mTagsToViewManagers.get(tag);
if (viewManager == null) { if (viewManager == null) {
throw new IllegalViewOperationException("ViewManager for tag " + tag + " could not be found"); throw new IllegalViewOperationException("ViewManager for tag " + tag + " could not be found");
@ -116,7 +116,7 @@ public class NativeViewHierarchyManager {
mLayoutAnimationEnabled = enabled; mLayoutAnimationEnabled = enabled;
} }
public void updateProperties(int tag, ReactStylesDiffMap props) { public synchronized void updateProperties(int tag, ReactStylesDiffMap props) {
UiThreadUtil.assertOnUiThread(); UiThreadUtil.assertOnUiThread();
try { try {
@ -128,7 +128,7 @@ public class NativeViewHierarchyManager {
} }
} }
public void updateViewExtraData(int tag, Object extraData) { public synchronized void updateViewExtraData(int tag, Object extraData) {
UiThreadUtil.assertOnUiThread(); UiThreadUtil.assertOnUiThread();
ViewManager viewManager = resolveViewManager(tag); ViewManager viewManager = resolveViewManager(tag);
@ -136,7 +136,7 @@ public class NativeViewHierarchyManager {
viewManager.updateExtraData(viewToUpdate, extraData); viewManager.updateExtraData(viewToUpdate, extraData);
} }
public void updateLayout( public synchronized void updateLayout(
int parentTag, int parentTag,
int tag, int tag,
int x, int x,
@ -200,7 +200,7 @@ public class NativeViewHierarchyManager {
} }
} }
public void createView( public synchronized void createView(
ThemedReactContext themedContext, ThemedReactContext themedContext,
int tag, int tag,
String className, String className,
@ -305,11 +305,12 @@ public class NativeViewHierarchyManager {
* a view which should be added at the specified index * a view which should be added at the specified index
* @param tagsToDelete list of tags corresponding to views that should be removed * @param tagsToDelete list of tags corresponding to views that should be removed
*/ */
public void manageChildren( public synchronized void manageChildren(
int tag, int tag,
@Nullable int[] indicesToRemove, @Nullable int[] indicesToRemove,
@Nullable ViewAtIndex[] viewsToAdd, @Nullable ViewAtIndex[] viewsToAdd,
@Nullable int[] tagsToDelete) { @Nullable int[] tagsToDelete) {
UiThreadUtil.assertOnUiThread();
final ViewGroup viewToManage = (ViewGroup) mTagsToViews.get(tag); final ViewGroup viewToManage = (ViewGroup) mTagsToViews.get(tag);
final ViewGroupManager viewManager = (ViewGroupManager) resolveViewManager(tag); final ViewGroupManager viewManager = (ViewGroupManager) resolveViewManager(tag);
if (viewToManage == null) { if (viewToManage == null) {
@ -463,9 +464,10 @@ public class NativeViewHierarchyManager {
/** /**
* Simplified version of manageChildren that only deals with adding children views * Simplified version of manageChildren that only deals with adding children views
*/ */
public void setChildren( public synchronized void setChildren(
int tag, int tag,
ReadableArray childrenTags) { ReadableArray childrenTags) {
UiThreadUtil.assertOnUiThread();
ViewGroup viewToManage = (ViewGroup) mTagsToViews.get(tag); ViewGroup viewToManage = (ViewGroup) mTagsToViews.get(tag);
ViewGroupManager viewManager = (ViewGroupManager) resolveViewManager(tag); ViewGroupManager viewManager = (ViewGroupManager) resolveViewManager(tag);
@ -486,21 +488,18 @@ public class NativeViewHierarchyManager {
/** /**
* See {@link UIManagerModule#addMeasuredRootView}. * See {@link UIManagerModule#addMeasuredRootView}.
*
* Must be called from the UI thread.
*/ */
public void addRootView( public synchronized void addRootView(
int tag, int tag,
SizeMonitoringFrameLayout view, SizeMonitoringFrameLayout view,
ThemedReactContext themedContext) { ThemedReactContext themedContext) {
addRootViewGroup(tag, view, themedContext); addRootViewGroup(tag, view, themedContext);
} }
protected final void addRootViewGroup( protected synchronized final void addRootViewGroup(
int tag, int tag,
ViewGroup view, ViewGroup view,
ThemedReactContext themedContext) { ThemedReactContext themedContext) {
UiThreadUtil.assertOnUiThread();
if (view.getId() != View.NO_ID) { if (view.getId() != View.NO_ID) {
throw new IllegalViewOperationException( throw new IllegalViewOperationException(
"Trying to add a root view with an explicit id already set. React Native uses " + "Trying to add a root view with an explicit id already set. React Native uses " +
@ -517,7 +516,7 @@ public class NativeViewHierarchyManager {
/** /**
* Releases all references to given native View. * Releases all references to given native View.
*/ */
protected void dropView(View view) { protected synchronized void dropView(View view) {
UiThreadUtil.assertOnUiThread(); UiThreadUtil.assertOnUiThread();
if (!mRootTags.get(view.getId())) { if (!mRootTags.get(view.getId())) {
// For non-root views we notify viewmanager with {@link ViewManager#onDropInstance} // For non-root views we notify viewmanager with {@link ViewManager#onDropInstance}
@ -539,7 +538,7 @@ public class NativeViewHierarchyManager {
mTagsToViewManagers.remove(view.getId()); mTagsToViewManagers.remove(view.getId());
} }
public void removeRootView(int rootViewTag) { public synchronized void removeRootView(int rootViewTag) {
UiThreadUtil.assertOnUiThread(); UiThreadUtil.assertOnUiThread();
if (!mRootTags.get(rootViewTag)) { if (!mRootTags.get(rootViewTag)) {
SoftAssertions.assertUnreachable( SoftAssertions.assertUnreachable(
@ -554,7 +553,7 @@ public class NativeViewHierarchyManager {
* Returns true on success, false on failure. If successful, after calling, output buffer will be * Returns true on success, false on failure. If successful, after calling, output buffer will be
* {x, y, width, height}. * {x, y, width, height}.
*/ */
public void measure(int tag, int[] outputBuffer) { public synchronized void measure(int tag, int[] outputBuffer) {
UiThreadUtil.assertOnUiThread(); UiThreadUtil.assertOnUiThread();
View v = mTagsToViews.get(tag); View v = mTagsToViews.get(tag);
if (v == null) { if (v == null) {
@ -587,7 +586,7 @@ public class NativeViewHierarchyManager {
* @param outputBuffer - output buffer that contains [x,y,width,height] of the view in coordinates * @param outputBuffer - output buffer that contains [x,y,width,height] of the view in coordinates
* relative to the device window * relative to the device window
*/ */
public void measureInWindow(int tag, int[] outputBuffer) { public synchronized void measureInWindow(int tag, int[] outputBuffer) {
UiThreadUtil.assertOnUiThread(); UiThreadUtil.assertOnUiThread();
View v = mTagsToViews.get(tag); View v = mTagsToViews.get(tag);
if (v == null) { if (v == null) {
@ -610,7 +609,8 @@ public class NativeViewHierarchyManager {
outputBuffer[3] = v.getHeight(); outputBuffer[3] = v.getHeight();
} }
public int findTargetTagForTouch(int reactTag, float touchX, float touchY) { public synchronized int findTargetTagForTouch(int reactTag, float touchX, float touchY) {
UiThreadUtil.assertOnUiThread();
View view = mTagsToViews.get(reactTag); View view = mTagsToViews.get(reactTag);
if (view == null) { if (view == null) {
throw new JSApplicationIllegalArgumentException("Could not find view with tag " + reactTag); throw new JSApplicationIllegalArgumentException("Could not find view with tag " + reactTag);
@ -618,7 +618,10 @@ public class NativeViewHierarchyManager {
return TouchTargetHelper.findTargetTagForTouch(touchX, touchY, (ViewGroup) view); return TouchTargetHelper.findTargetTagForTouch(touchX, touchY, (ViewGroup) view);
} }
public void setJSResponder(int reactTag, int initialReactTag, boolean blockNativeResponder) { public synchronized void setJSResponder(
int reactTag,
int initialReactTag,
boolean blockNativeResponder) {
if (!blockNativeResponder) { if (!blockNativeResponder) {
mJSResponderHandler.setJSResponder(initialReactTag, null); mJSResponderHandler.setJSResponder(initialReactTag, null);
return; return;
@ -652,7 +655,7 @@ public class NativeViewHierarchyManager {
mLayoutAnimator.reset(); mLayoutAnimator.reset();
} }
/* package */ void startAnimationForNativeView( /* package */ synchronized void startAnimationForNativeView(
int reactTag, int reactTag,
Animation animation, Animation animation,
@Nullable final Callback animationCallback) { @Nullable final Callback animationCallback) {
@ -691,7 +694,10 @@ public class NativeViewHierarchyManager {
} }
} }
public void dispatchCommand(int reactTag, int commandId, @Nullable ReadableArray args) { public synchronized void dispatchCommand(
int reactTag,
int commandId,
@Nullable ReadableArray args) {
UiThreadUtil.assertOnUiThread(); UiThreadUtil.assertOnUiThread();
View view = mTagsToViews.get(reactTag); View view = mTagsToViews.get(reactTag);
if (view == null) { if (view == null) {
@ -712,7 +718,7 @@ public class NativeViewHierarchyManager {
* @param success will be called with the position of the selected item as the first argument, or * @param success will be called with the position of the selected item as the first argument, or
* no arguments if the menu is dismissed * no arguments if the menu is dismissed
*/ */
public void showPopupMenu(int reactTag, ReadableArray items, Callback success) { public synchronized void showPopupMenu(int reactTag, ReadableArray items, Callback success) {
UiThreadUtil.assertOnUiThread(); UiThreadUtil.assertOnUiThread();
View anchor = mTagsToViews.get(reactTag); View anchor = mTagsToViews.get(reactTag);
if (anchor == null) { if (anchor == null) {
@ -780,5 +786,4 @@ public class NativeViewHierarchyManager {
} }
AccessibilityHelper.sendAccessibilityEvent(view, eventType); AccessibilityHelper.sendAccessibilityEvent(view, eventType);
} }
} }

View File

@ -567,26 +567,7 @@ public class UIViewOperationQueue {
final int tag, final int tag,
final SizeMonitoringFrameLayout rootView, final SizeMonitoringFrameLayout rootView,
final ThemedReactContext themedRootContext) { final ThemedReactContext themedRootContext) {
if (UiThreadUtil.isOnUiThread()) {
mNativeViewHierarchyManager.addRootView(tag, rootView, themedRootContext); 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);
}
}
} }
/** /**