diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index 903db80d5c0..10388444d06 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -3673,6 +3673,7 @@ public abstract interface class com/facebook/react/runtime/internal/bolts/Contin public class com/facebook/react/runtime/internal/bolts/Task : com/facebook/react/interfaces/TaskInterface { public static final field BACKGROUND_EXECUTOR Ljava/util/concurrent/ExecutorService; + public static final field UI_THREAD_CONDITIONAL_SYNC_EXECUTOR Ljava/util/concurrent/Executor; public static final field UI_THREAD_EXECUTOR Ljava/util/concurrent/Executor; public static fun call (Ljava/util/concurrent/Callable;)Lcom/facebook/react/runtime/internal/bolts/Task; public static fun call (Ljava/util/concurrent/Callable;Lcom/facebook/react/runtime/internal/bolts/CancellationToken;)Lcom/facebook/react/runtime/internal/bolts/Task; diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/internal/bolts/AndroidExecutors.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/internal/bolts/AndroidExecutors.java index 49518ccc90d..dc4e691dc0b 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/internal/bolts/AndroidExecutors.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/internal/bolts/AndroidExecutors.java @@ -39,9 +39,11 @@ final class AndroidExecutors { private static final AndroidExecutors INSTANCE = new AndroidExecutors(); @NonNull private final Executor uiThread; + @NonNull private final Executor uiThreadConditionalSync; private AndroidExecutors() { uiThread = new UIThreadExecutor(); + uiThreadConditionalSync = new UIThreadConditionalSyncExecutor(); } /** @@ -122,16 +124,52 @@ final class AndroidExecutors { } } - /** An {@link java.util.concurrent.Executor} that executes tasks on the UI thread. */ + /** + * An {@link java.util.concurrent.Executor} that schedules tasks to run asynchronously on the UI + * thread. + */ public static @NonNull Executor uiThread() { return INSTANCE.uiThread; } - /** An {@link java.util.concurrent.Executor} that runs tasks on the UI thread. */ + /** + * An {@link java.util.concurrent.Executor} that runs tasks on the UI thread (immediately if + * already on that thread). + */ + public static @NonNull Executor uiThreadConditionalSync() { + return INSTANCE.uiThreadConditionalSync; + } + + /** + * An {@link java.util.concurrent.Executor} that schedules tasks to run asynchronously on the UI + * thread. + */ private static class UIThreadExecutor implements Executor { + private final Handler mHandler = new Handler(Looper.getMainLooper()); + @Override public void execute(@NonNull Runnable command) { - new Handler(Looper.getMainLooper()).post(command); + // Otherwise, post it on the main thread handler + mHandler.post(command); + } + } + + /** + * An {@link java.util.concurrent.Executor} that runs tasks on the UI thread (immediately if + * already on that thread). + */ + private static class UIThreadConditionalSyncExecutor implements Executor { + private final Handler mHandler = new Handler(Looper.getMainLooper()); + + @Override + public void execute(@NonNull Runnable command) { + if (Looper.myLooper() == mHandler.getLooper()) { + // If we're already on the main thread, execute the command immediately + command.run(); + } else { + // Otherwise, post it on the main thread handler + mHandler.post(command); + } } } } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/internal/bolts/Task.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/internal/bolts/Task.java index 1f4db71e336..634fb318a15 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/internal/bolts/Task.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/internal/bolts/Task.java @@ -42,6 +42,10 @@ public class Task implements TaskInterface { /** An {@link java.util.concurrent.Executor} that executes tasks on the UI thread. */ public static final Executor UI_THREAD_EXECUTOR = AndroidExecutors.uiThread(); + /** An {@link java.util.concurrent.Executor} that executes tasks on the UI thread. */ + public static final Executor UI_THREAD_CONDITIONAL_SYNC_EXECUTOR = + AndroidExecutors.uiThreadConditionalSync(); + /** * Interface for handlers invoked when a failed {@code Task} is about to be finalized, but the * exception has not been consumed.