From 6138e203799f0e5178c4831cb5dc07c58424b713 Mon Sep 17 00:00:00 2001 From: Aaron Chiu Date: Fri, 21 Apr 2017 05:25:20 -0700 Subject: [PATCH] give us the ability to create a background UI thread Reviewed By: javache Differential Revision: D4928689 fbshipit-source-id: 94fa5f3f7c047ad816c7699dcc8bebf9e751b282 --- .../facebook/react/bridge/ReactContext.java | 18 ++++++++ .../bridge/queue/MessageQueueThreadImpl.java | 40 +++++++++++++----- .../bridge/queue/MessageQueueThreadSpec.java | 5 +++ .../bridge/queue/ReactQueueConfiguration.java | 4 ++ .../queue/ReactQueueConfigurationImpl.java | 31 +++++++++++--- .../queue/ReactQueueConfigurationSpec.java | 41 ++++++++++++++++--- .../react/cxxbridge/JavaModuleWrapper.java | 2 +- .../react/uimanager/UIManagerModule.java | 4 +- .../react/views/modal/ReactModalHostView.java | 2 +- 9 files changed, 120 insertions(+), 27 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactContext.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactContext.java index 96f561315..3c3c0ef8b 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactContext.java +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactContext.java @@ -47,6 +47,7 @@ public class ReactContext extends ContextWrapper { private @Nullable CatalystInstance mCatalystInstance; private @Nullable LayoutInflater mInflater; private @Nullable MessageQueueThread mUiMessageQueueThread; + private @Nullable MessageQueueThread mUiBackgroundMessageQueueThread; private @Nullable MessageQueueThread mNativeModulesMessageQueueThread; private @Nullable MessageQueueThread mJSMessageQueueThread; private @Nullable NativeModuleCallExceptionHandler mNativeModuleCallExceptionHandler; @@ -71,6 +72,7 @@ public class ReactContext extends ContextWrapper { ReactQueueConfiguration queueConfig = catalystInstance.getReactQueueConfiguration(); mUiMessageQueueThread = queueConfig.getUIQueueThread(); + mUiBackgroundMessageQueueThread = queueConfig.getUIBackgroundQueueThread(); mNativeModulesMessageQueueThread = queueConfig.getNativeModulesQueueThread(); mJSMessageQueueThread = queueConfig.getJSQueueThread(); } @@ -279,6 +281,14 @@ public class ReactContext extends ContextWrapper { Assertions.assertNotNull(mUiMessageQueueThread).runOnQueue(runnable); } + public void assertOnUiBackgroundQueueThread() { + Assertions.assertNotNull(mUiBackgroundMessageQueueThread).assertIsOnThread(); + } + + public void runOnUiBackgroundQueueThread(Runnable runnable) { + Assertions.assertNotNull(mUiBackgroundMessageQueueThread).runOnQueue(runnable); + } + public void assertOnNativeModulesQueueThread() { Assertions.assertNotNull(mNativeModulesMessageQueueThread).assertIsOnThread(); } @@ -307,6 +317,14 @@ public class ReactContext extends ContextWrapper { Assertions.assertNotNull(mJSMessageQueueThread).runOnQueue(runnable); } + public void runUIBackgroundRunnable(Runnable runnable) { + if (mUiBackgroundMessageQueueThread == null) { + runOnNativeModulesQueueThread(runnable); + } else { + runOnUiBackgroundQueueThread(runnable); + } + } + /** * Passes the given exception to the current * {@link com.facebook.react.bridge.NativeModuleCallExceptionHandler} if one exists, rethrowing diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/queue/MessageQueueThreadImpl.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/queue/MessageQueueThreadImpl.java index 7a94d0019..041a2d8cc 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/queue/MessageQueueThreadImpl.java +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/queue/MessageQueueThreadImpl.java @@ -61,7 +61,6 @@ public class MessageQueueThreadImpl implements MessageQueueThread { mHandler.post(runnable); } - @DoNotStrip @Override public Future callOnQueue(final Callable callable) { @@ -143,6 +142,8 @@ public class MessageQueueThreadImpl implements MessageQueueThread { switch (spec.getThreadType()) { case MAIN_UI: return createForMainThread(spec.getName(), exceptionHandler); + case BACKGROUND_UI: + return startUIBackgroundThread(spec.getName(), spec.getStackSize(), exceptionHandler); case NEW_BACKGROUND: return startNewBackgroundThread(spec.getName(), spec.getStackSize(), exceptionHandler); default: @@ -176,13 +177,27 @@ public class MessageQueueThreadImpl implements MessageQueueThread { return mqt; } + public static MessageQueueThreadImpl startUIBackgroundThread( + final String name, + long stackSize, + QueueThreadExceptionHandler exceptionHandler) { + return startNewBackgroundThread(name, stackSize, exceptionHandler, true); + } + public static MessageQueueThreadImpl startNewBackgroundThread( - final String name, - QueueThreadExceptionHandler exceptionHandler) { + final String name, + QueueThreadExceptionHandler exceptionHandler) { return startNewBackgroundThread( - name, - MessageQueueThreadSpec.DEFAULT_STACK_SIZE_BYTES, - exceptionHandler); + name, + MessageQueueThreadSpec.DEFAULT_STACK_SIZE_BYTES, + exceptionHandler); + } + + public static MessageQueueThreadImpl startNewBackgroundThread( + final String name, + long stackSize, + QueueThreadExceptionHandler exceptionHandler) { + return startNewBackgroundThread(name, stackSize, exceptionHandler, false); } /** @@ -190,17 +205,20 @@ public class MessageQueueThreadImpl implements MessageQueueThread { * running on it. Give it a name for easier debugging and optionally a suggested stack size. * When this method exits, the new MessageQueueThreadImpl is ready to receive events. */ - public static MessageQueueThreadImpl startNewBackgroundThread( - final String name, - long stackSize, - QueueThreadExceptionHandler exceptionHandler) { + private static MessageQueueThreadImpl startNewBackgroundThread( + final String name, + long stackSize, + QueueThreadExceptionHandler exceptionHandler, + final boolean forUIManagerModule) { final SimpleSettableFuture looperFuture = new SimpleSettableFuture<>(); final SimpleSettableFuture mqtFuture = new SimpleSettableFuture<>(); Thread bgThread = new Thread(null, new Runnable() { @Override public void run() { - Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); + Process.setThreadPriority(forUIManagerModule ? + Process.THREAD_PRIORITY_DEFAULT + Process.THREAD_PRIORITY_MORE_FAVORABLE : + Process.THREAD_PRIORITY_DEFAULT); Looper.prepare(); looperFuture.set(Looper.myLooper()); diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/queue/MessageQueueThreadSpec.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/queue/MessageQueueThreadSpec.java index 1ff61fdf4..f99c18e01 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/queue/MessageQueueThreadSpec.java +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/queue/MessageQueueThreadSpec.java @@ -22,9 +22,14 @@ public class MessageQueueThreadSpec { protected static enum ThreadType { MAIN_UI, + BACKGROUND_UI, NEW_BACKGROUND, } + public static MessageQueueThreadSpec newUIBackgroundTreadSpec(String name) { + return new MessageQueueThreadSpec(ThreadType.BACKGROUND_UI, name); + } + public static MessageQueueThreadSpec newBackgroundThreadSpec(String name) { return new MessageQueueThreadSpec(ThreadType.NEW_BACKGROUND, name); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/queue/ReactQueueConfiguration.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/queue/ReactQueueConfiguration.java index 6e40d4ba5..1cab51369 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/queue/ReactQueueConfiguration.java +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/queue/ReactQueueConfiguration.java @@ -9,6 +9,8 @@ package com.facebook.react.bridge.queue; +import javax.annotation.Nullable; + /** * Specifies which {@link MessageQueueThread}s must be used to run the various contexts of * execution within catalyst (Main UI thread, native modules, and JS). Some of these queues *may* be @@ -20,6 +22,8 @@ package com.facebook.react.bridge.queue; */ public interface ReactQueueConfiguration { MessageQueueThread getUIQueueThread(); + @Nullable + MessageQueueThread getUIBackgroundQueueThread(); MessageQueueThread getNativeModulesQueueThread(); MessageQueueThread getJSQueueThread(); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/queue/ReactQueueConfigurationImpl.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/queue/ReactQueueConfigurationImpl.java index 00af88372..367fec1ae 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/queue/ReactQueueConfigurationImpl.java +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/queue/ReactQueueConfigurationImpl.java @@ -9,6 +9,8 @@ package com.facebook.react.bridge.queue; +import javax.annotation.Nullable; + import java.util.Map; import android.os.Looper; @@ -18,14 +20,17 @@ import com.facebook.react.common.MapBuilder; public class ReactQueueConfigurationImpl implements ReactQueueConfiguration { private final MessageQueueThreadImpl mUIQueueThread; + private final @Nullable MessageQueueThreadImpl mUIBackgroundQueueThread; private final MessageQueueThreadImpl mNativeModulesQueueThread; private final MessageQueueThreadImpl mJSQueueThread; private ReactQueueConfigurationImpl( - MessageQueueThreadImpl uiQueueThread, - MessageQueueThreadImpl nativeModulesQueueThread, - MessageQueueThreadImpl jsQueueThread) { + MessageQueueThreadImpl uiQueueThread, + @Nullable MessageQueueThreadImpl uiBackgroundQueueThread, + MessageQueueThreadImpl nativeModulesQueueThread, + MessageQueueThreadImpl jsQueueThread) { mUIQueueThread = uiQueueThread; + mUIBackgroundQueueThread = uiBackgroundQueueThread; mNativeModulesQueueThread = nativeModulesQueueThread; mJSQueueThread = jsQueueThread; } @@ -35,6 +40,11 @@ public class ReactQueueConfigurationImpl implements ReactQueueConfiguration { return mUIQueueThread; } + @Override + public @Nullable MessageQueueThread getUIBackgroundQueueThread() { + return mUIBackgroundQueueThread; + } + @Override public MessageQueueThread getNativeModulesQueueThread() { return mNativeModulesQueueThread; @@ -65,7 +75,7 @@ public class ReactQueueConfigurationImpl implements ReactQueueConfiguration { MessageQueueThreadSpec uiThreadSpec = MessageQueueThreadSpec.mainThreadSpec(); MessageQueueThreadImpl uiThread = - MessageQueueThreadImpl.create( uiThreadSpec, exceptionHandler); + MessageQueueThreadImpl.create(uiThreadSpec, exceptionHandler); specsToThreads.put(uiThreadSpec, uiThread); MessageQueueThreadImpl jsThread = specsToThreads.get(spec.getJSQueueThreadSpec()); @@ -80,6 +90,17 @@ public class ReactQueueConfigurationImpl implements ReactQueueConfiguration { MessageQueueThreadImpl.create(spec.getNativeModulesQueueThreadSpec(), exceptionHandler); } - return new ReactQueueConfigurationImpl(uiThread, nativeModulesThread, jsThread); + MessageQueueThreadImpl uiBackgroundThread = + specsToThreads.get(spec.getUIBackgroundQueueThreadSpec()); + if (uiBackgroundThread == null && spec.getUIBackgroundQueueThreadSpec() != null) { + uiBackgroundThread = + MessageQueueThreadImpl.create(spec.getUIBackgroundQueueThreadSpec(), exceptionHandler); + } + + return new ReactQueueConfigurationImpl( + uiThread, + uiBackgroundThread, + nativeModulesThread, + jsThread); } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/queue/ReactQueueConfigurationSpec.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/queue/ReactQueueConfigurationSpec.java index 133504887..6b23bfd71 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/queue/ReactQueueConfigurationSpec.java +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/queue/ReactQueueConfigurationSpec.java @@ -25,16 +25,23 @@ public class ReactQueueConfigurationSpec { private static final long LEGACY_STACK_SIZE_BYTES = 2000000; + private final @Nullable MessageQueueThreadSpec mUIBackgroundQueueThreadSpec; private final MessageQueueThreadSpec mNativeModulesQueueThreadSpec; private final MessageQueueThreadSpec mJSQueueThreadSpec; private ReactQueueConfigurationSpec( - MessageQueueThreadSpec nativeModulesQueueThreadSpec, - MessageQueueThreadSpec jsQueueThreadSpec) { + @Nullable MessageQueueThreadSpec uiBackgroundQueueThreadSpec, + MessageQueueThreadSpec nativeModulesQueueThreadSpec, + MessageQueueThreadSpec jsQueueThreadSpec) { + mUIBackgroundQueueThreadSpec = uiBackgroundQueueThreadSpec; mNativeModulesQueueThreadSpec = nativeModulesQueueThreadSpec; mJSQueueThreadSpec = jsQueueThreadSpec; } + public @Nullable MessageQueueThreadSpec getUIBackgroundQueueThreadSpec() { + return mUIBackgroundQueueThreadSpec; + } + public MessageQueueThreadSpec getNativeModulesQueueThreadSpec() { return mNativeModulesQueueThreadSpec; } @@ -57,15 +64,36 @@ public class ReactQueueConfigurationSpec { .build(); } + public static ReactQueueConfigurationSpec createWithSeparateUIBackgroundThread() { + MessageQueueThreadSpec spec = Build.VERSION.SDK_INT < 21 ? + MessageQueueThreadSpec.newBackgroundThreadSpec("native_modules", LEGACY_STACK_SIZE_BYTES) : + MessageQueueThreadSpec.newBackgroundThreadSpec("native_modules"); + return builder() + .setJSQueueThreadSpec(MessageQueueThreadSpec.newBackgroundThreadSpec("js")) + .setNativeModulesQueueThreadSpec(spec) + .setUIBackgroundQueueThreadSpec( + MessageQueueThreadSpec.newUIBackgroundTreadSpec("ui_background")) + .build(); + } + public static class Builder { + private @Nullable MessageQueueThreadSpec mUIBackgroundQueueSpec; private @Nullable MessageQueueThreadSpec mNativeModulesQueueSpec; private @Nullable MessageQueueThreadSpec mJSQueueSpec; + public Builder setUIBackgroundQueueThreadSpec(MessageQueueThreadSpec spec) { + Assertions.assertCondition( + mUIBackgroundQueueSpec == null, + "Setting UI background queue multiple times!"); + mUIBackgroundQueueSpec = spec; + return this; + } + public Builder setNativeModulesQueueThreadSpec(MessageQueueThreadSpec spec) { Assertions.assertCondition( - mNativeModulesQueueSpec == null, - "Setting native modules queue spec multiple times!"); + mNativeModulesQueueSpec == null, + "Setting native modules queue spec multiple times!"); mNativeModulesQueueSpec = spec; return this; } @@ -78,8 +106,9 @@ public class ReactQueueConfigurationSpec { public ReactQueueConfigurationSpec build() { return new ReactQueueConfigurationSpec( - Assertions.assertNotNull(mNativeModulesQueueSpec), - Assertions.assertNotNull(mJSQueueSpec)); + mUIBackgroundQueueSpec, + Assertions.assertNotNull(mNativeModulesQueueSpec), + Assertions.assertNotNull(mJSQueueSpec)); } } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/JavaModuleWrapper.java b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/JavaModuleWrapper.java index 4858568cb..6bab79b6a 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/JavaModuleWrapper.java +++ b/ReactAndroid/src/main/java/com/facebook/react/cxxbridge/JavaModuleWrapper.java @@ -15,8 +15,8 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashSet; import java.util.List; -import java.util.Set; import java.util.Map; +import java.util.Set; import com.facebook.proguard.annotations.DoNotStrip; import com.facebook.react.bridge.BaseJavaModule; 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 bde687d52..702a6b539 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java @@ -31,10 +31,8 @@ import com.facebook.react.bridge.ReactMarker; import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.bridge.WritableMap; import com.facebook.react.common.ReactConstants; import com.facebook.react.module.annotations.ReactModule; -import com.facebook.react.modules.core.DeviceEventManagerModule; import com.facebook.react.uimanager.debug.NotThreadSafeViewHierarchyUpdateDebugListener; import com.facebook.react.uimanager.events.EventDispatcher; import com.facebook.systrace.Systrace; @@ -212,7 +210,7 @@ public class UIManagerModule extends ReactContextBaseJavaModule implements new SizeMonitoringFrameLayout.OnSizeChangedListener() { @Override public void onSizeChanged(final int width, final int height, int oldW, int oldH) { - reactApplicationContext.runOnNativeModulesQueueThread( + reactApplicationContext.runUIBackgroundRunnable( new GuardedRunnable(reactApplicationContext) { @Override public void runGuarded() { diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.java b/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.java index 674c6ef80..705e45502 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/modal/ReactModalHostView.java @@ -306,7 +306,7 @@ public class ReactModalHostView extends ViewGroup implements LifecycleEventListe if (getChildCount() > 0) { final int viewTag = getChildAt(0).getId(); ReactContext reactContext = (ReactContext) getContext(); - reactContext.runOnNativeModulesQueueThread( + reactContext.runUIBackgroundRunnable( new GuardedRunnable(reactContext) { @Override public void runGuarded() {