diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/annotations/FrameworkAPI.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/annotations/FrameworkAPI.kt index 37e2863356003f..078552af0f12e2 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/annotations/FrameworkAPI.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/annotations/FrameworkAPI.kt @@ -8,7 +8,7 @@ package com.facebook.react.common.annotations @Retention(AnnotationRetention.RUNTIME) -@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION) +@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.CONSTRUCTOR) @RequiresOptIn( level = RequiresOptIn.Level.ERROR, message = diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DefaultDevSupportManagerFactory.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DefaultDevSupportManagerFactory.kt index ac7d5daabe8bb3..dfc121ebd4f67a 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DefaultDevSupportManagerFactory.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DefaultDevSupportManagerFactory.kt @@ -9,12 +9,15 @@ package com.facebook.react.devsupport import android.content.Context import com.facebook.react.common.SurfaceDelegateFactory +import com.facebook.react.common.annotations.FrameworkAPI import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener import com.facebook.react.devsupport.interfaces.DevLoadingViewManager import com.facebook.react.devsupport.interfaces.DevSupportManager import com.facebook.react.devsupport.interfaces.PausedInDebuggerOverlayManager import com.facebook.react.devsupport.interfaces.RedBoxHandler import com.facebook.react.packagerconnection.RequestHandler +import com.facebook.react.runtime.BridgelessDevSupportManager +import com.facebook.react.runtime.ReactHostImpl /** * A simple factory that creates instances of [DevSupportManager] implementations. Uses reflection @@ -23,93 +26,102 @@ import com.facebook.react.packagerconnection.RequestHandler */ public class DefaultDevSupportManagerFactory : DevSupportManagerFactory { - @Deprecated( - "in favor of the customisable create for DevSupportManagerFactory", - ReplaceWith( - "create(applicationContext, reactInstanceManagerHelper, packagerPathForJSBundleName, enableOnCreate, redBoxHandler, devBundleDownloadListener, minNumShakes, customPackagerCommandHandlers, surfaceDelegateFactory, devLoadingViewManager, pausedInDebuggerOverlayManager)")) - public fun create( - applicationContext: Context, - reactInstanceDevHelper: ReactInstanceDevHelper, - packagerPathForJSBundleName: String?, - enableOnCreate: Boolean, - minNumShakes: Int - ): DevSupportManager { - return create( - applicationContext, - reactInstanceDevHelper, - packagerPathForJSBundleName, - enableOnCreate, - null, - null, - minNumShakes, - null, - null, - null, - null) - } - public override fun create( - applicationContext: Context, - reactInstanceManagerHelper: ReactInstanceDevHelper, - packagerPathForJSBundleName: String?, - enableOnCreate: Boolean, - redBoxHandler: RedBoxHandler?, - devBundleDownloadListener: DevBundleDownloadListener?, - minNumShakes: Int, - customPackagerCommandHandlers: Map?, - surfaceDelegateFactory: SurfaceDelegateFactory?, - devLoadingViewManager: DevLoadingViewManager?, - pausedInDebuggerOverlayManager: PausedInDebuggerOverlayManager? + applicationContext: Context, + reactInstanceManagerHelper: ReactInstanceDevHelper, + packagerPathForJSBundleName: String?, + enableOnCreate: Boolean, + redBoxHandler: RedBoxHandler?, + devBundleDownloadListener: DevBundleDownloadListener?, + minNumShakes: Int, + customPackagerCommandHandlers: Map?, + surfaceDelegateFactory: SurfaceDelegateFactory?, + devLoadingViewManager: DevLoadingViewManager?, + pausedInDebuggerOverlayManager: PausedInDebuggerOverlayManager? ): DevSupportManager { return if (!enableOnCreate) { ReleaseDevSupportManager() } else - try { - // Developer support is enabled, we now must choose whether to return a DevSupportManager, - // or a more lean profiling-only PerftestDevSupportManager. We make the choice by first - // trying to return the full support DevSupportManager and if it fails, then just - // return PerftestDevSupportManager. + try { + // Developer support is enabled, we now must choose whether to return a DevSupportManager, + // or a more lean profiling-only PerftestDevSupportManager. We make the choice by first + // trying to return the full support DevSupportManager and if it fails, then just + // return PerftestDevSupportManager. - // ProGuard is surprisingly smart in this case and will keep a class if it detects a call - // to - // Class.forName() with a static string. So instead we generate a quasi-dynamic string to - // confuse it. - val className = - StringBuilder(DEVSUPPORT_IMPL_PACKAGE) - .append(".") - .append(DEVSUPPORT_IMPL_CLASS) - .toString() - val devSupportManagerClass = Class.forName(className) - val constructor = - devSupportManagerClass.getConstructor( - Context::class.java, - ReactInstanceDevHelper::class.java, - String::class.java, - Boolean::class.javaPrimitiveType, - RedBoxHandler::class.java, - DevBundleDownloadListener::class.java, - Int::class.javaPrimitiveType, - MutableMap::class.java, - SurfaceDelegateFactory::class.java, - DevLoadingViewManager::class.java, - PausedInDebuggerOverlayManager::class.java) - constructor.newInstance( - applicationContext, - reactInstanceManagerHelper, - packagerPathForJSBundleName, - true, - redBoxHandler, - devBundleDownloadListener, - minNumShakes, - customPackagerCommandHandlers, - surfaceDelegateFactory, - devLoadingViewManager, - pausedInDebuggerOverlayManager) as DevSupportManager - } catch (e: Exception) { - PerftestDevSupportManager(applicationContext) - } + // ProGuard is surprisingly smart in this case and will keep a class if it detects a call + // to + // Class.forName() with a static string. So instead we generate a quasi-dynamic string to + // confuse it. + val className = + StringBuilder(DEVSUPPORT_IMPL_PACKAGE) + .append(".") + .append(DEVSUPPORT_IMPL_CLASS) + .toString() + val devSupportManagerClass = Class.forName(className) + val constructor = + devSupportManagerClass.getConstructor( + Context::class.java, + ReactInstanceDevHelper::class.java, + String::class.java, + Boolean::class.javaPrimitiveType, + RedBoxHandler::class.java, + DevBundleDownloadListener::class.java, + Int::class.javaPrimitiveType, + MutableMap::class.java, + SurfaceDelegateFactory::class.java, + DevLoadingViewManager::class.java, + PausedInDebuggerOverlayManager::class.java) + constructor.newInstance( + applicationContext, + reactInstanceManagerHelper, + packagerPathForJSBundleName, + true, + redBoxHandler, + devBundleDownloadListener, + minNumShakes, + customPackagerCommandHandlers, + surfaceDelegateFactory, + devLoadingViewManager, + pausedInDebuggerOverlayManager) as DevSupportManager + } catch (e: Exception) { + PerftestDevSupportManager(applicationContext) + } } + @FrameworkAPI + override fun create( + reactHost: ReactHostImpl, + applicationContext: Context, + reactInstanceManagerHelper: ReactInstanceDevHelper, + packagerPathForJSBundleName: String?, + enableOnCreate: Boolean, + redBoxHandler: RedBoxHandler?, + devBundleDownloadListener: DevBundleDownloadListener?, + minNumShakes: Int, + customPackagerCommandHandlers: MutableMap?, + surfaceDelegateFactory: SurfaceDelegateFactory?, + devLoadingViewManager: DevLoadingViewManager?, + pausedInDebuggerOverlayManager: PausedInDebuggerOverlayManager?, + useDevSupport: Boolean + ): DevSupportManager = + if (!useDevSupport) { + ReleaseDevSupportManager() + } else { + BridgelessDevSupportManager( + reactHost, + applicationContext, + reactInstanceManagerHelper, + packagerPathForJSBundleName, + enableOnCreate, + redBoxHandler, + devBundleDownloadListener, + minNumShakes, + customPackagerCommandHandlers, + surfaceDelegateFactory, + devLoadingViewManager, + pausedInDebuggerOverlayManager) + } + private companion object { private const val DEVSUPPORT_IMPL_PACKAGE = "com.facebook.react.devsupport" private const val DEVSUPPORT_IMPL_CLASS = "BridgeDevSupportManager" diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerFactory.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerFactory.java index 9d0d120a16b6b6..6d96efb5fcdd12 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerFactory.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerFactory.java @@ -16,19 +16,43 @@ import com.facebook.react.devsupport.interfaces.PausedInDebuggerOverlayManager; import com.facebook.react.devsupport.interfaces.RedBoxHandler; import com.facebook.react.packagerconnection.RequestHandler; +import com.facebook.react.runtime.ReactHostImpl; import java.util.Map; public interface DevSupportManagerFactory { + /** + * Factory used by the Old Architecture flow to create a {@link DevSupportManager} and a {@link + * com.facebook.react.runtime.BridgeDevSupportManager} + */ DevSupportManager create( - Context applicationContext, - ReactInstanceDevHelper reactInstanceManagerHelper, - @Nullable String packagerPathForJSBundleName, - boolean enableOnCreate, - @Nullable RedBoxHandler redBoxHandler, - @Nullable DevBundleDownloadListener devBundleDownloadListener, - int minNumShakes, - @Nullable Map customPackagerCommandHandlers, - @Nullable SurfaceDelegateFactory surfaceDelegateFactory, - @Nullable DevLoadingViewManager devLoadingViewManager, - @Nullable PausedInDebuggerOverlayManager pausedInDebuggerOverlayManager); + Context applicationContext, + ReactInstanceDevHelper reactInstanceManagerHelper, + @Nullable String packagerPathForJSBundleName, + boolean enableOnCreate, + @Nullable RedBoxHandler redBoxHandler, + @Nullable DevBundleDownloadListener devBundleDownloadListener, + int minNumShakes, + @Nullable Map customPackagerCommandHandlers, + @Nullable SurfaceDelegateFactory surfaceDelegateFactory, + @Nullable DevLoadingViewManager devLoadingViewManager, + @Nullable PausedInDebuggerOverlayManager pausedInDebuggerOverlayManager); + + /** + * Factory used by the New Architecture/Bridgeless flow to create a {@link DevSupportManager} and + * a {@link com.facebook.react.runtime.BridgelessDevSupportManager} + */ + DevSupportManager create( + ReactHostImpl host, + Context applicationContext, + ReactInstanceDevHelper reactInstanceManagerHelper, + @Nullable String packagerPathForJSBundleName, + boolean enableOnCreate, + @Nullable RedBoxHandler redBoxHandler, + @Nullable DevBundleDownloadListener devBundleDownloadListener, + int minNumShakes, + @Nullable Map customPackagerCommandHandlers, + @Nullable SurfaceDelegateFactory surfaceDelegateFactory, + @Nullable DevLoadingViewManager devLoadingViewManager, + @Nullable PausedInDebuggerOverlayManager pausedInDebuggerOverlayManager, + boolean useDevSupport); } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/BridgelessDevSupportManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/BridgelessDevSupportManager.java index b411f73f9e57dc..eab44bc55120cb 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/BridgelessDevSupportManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/BridgelessDevSupportManager.java @@ -18,13 +18,20 @@ import com.facebook.react.bridge.JavaScriptExecutorFactory; import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.UiThreadUtil; +import com.facebook.react.common.SurfaceDelegateFactory; import com.facebook.react.devsupport.DevSupportManagerBase; import com.facebook.react.devsupport.HMRClient; import com.facebook.react.devsupport.ReactInstanceDevHelper; +import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener; +import com.facebook.react.devsupport.interfaces.DevLoadingViewManager; import com.facebook.react.devsupport.interfaces.DevSplitBundleCallback; +import com.facebook.react.devsupport.interfaces.PausedInDebuggerOverlayManager; +import com.facebook.react.devsupport.interfaces.RedBoxHandler; import com.facebook.react.modules.core.DeviceEventManagerModule; +import com.facebook.react.packagerconnection.RequestHandler; import com.facebook.react.runtime.internal.bolts.Continuation; import com.facebook.react.runtime.internal.bolts.Task; +import java.util.Map; /** * An implementation of {@link com.facebook.react.devsupport.interfaces.DevSupportManager} that @@ -32,24 +39,56 @@ * APIs for asynchronously loading the JS bundle. */ @Nullsafe(Nullsafe.Mode.LOCAL) -class BridgelessDevSupportManager extends DevSupportManagerBase { +public class BridgelessDevSupportManager extends DevSupportManagerBase { private final ReactHostImpl mReactHost; public BridgelessDevSupportManager( - ReactHostImpl host, Context context, @Nullable String packagerPathForJSBundleName) { + ReactHostImpl host, Context context, @Nullable String packagerPathForJSBundleName) { + this( + host, + context.getApplicationContext(), + createInstanceDevHelper(host), + packagerPathForJSBundleName, + true /* enableOnCreate */, + null /* redBoxHandler */, + null /* devBundleDownloadListener */, + 2 /* minNumShakes */, + null /* customPackagerCommandHandlers */, + null /* surfaceDelegateFactory */, + null /* devLoadingViewManager */, + null /* pausedInDebuggerOverlayManager */); + } + + /** + * This constructor mirrors the same constructor we have for {@link BridgeDevSupportManager} and + * is kept for backward compatibility. + */ + public BridgelessDevSupportManager( + ReactHostImpl host, + Context applicationContext, + ReactInstanceDevHelper reactInstanceManagerHelper, + @Nullable String packagerPathForJSBundleName, + boolean enableOnCreate, + @Nullable RedBoxHandler redBoxHandler, + @Nullable DevBundleDownloadListener devBundleDownloadListener, + int minNumShakes, + @Nullable Map customPackagerCommandHandlers, + @Nullable SurfaceDelegateFactory surfaceDelegateFactory, + @Nullable DevLoadingViewManager devLoadingViewManager, + @Nullable PausedInDebuggerOverlayManager pausedInDebuggerOverlayManager) { super( - context.getApplicationContext(), - createInstanceDevHelper(host), - packagerPathForJSBundleName, - true /* enableOnCreate */, - null /* redBoxHandler */, - null /* devBundleDownloadListener */, - 2 /* minNumShakes */, - null /* customPackagerCommandHandlers */, - null /* surfaceDelegateFactory */, - null /* devLoadingViewManager */, - null /* pausedInDebuggerOverlayManager */); + applicationContext, + reactInstanceManagerHelper, + packagerPathForJSBundleName, + enableOnCreate, + redBoxHandler, + devBundleDownloadListener, + minNumShakes, + customPackagerCommandHandlers, + surfaceDelegateFactory, + devLoadingViewManager, + pausedInDebuggerOverlayManager); mReactHost = host; } @@ -60,37 +99,37 @@ protected String getUniqueTag() { @Override public void loadSplitBundleFromServer( - final String bundlePath, final DevSplitBundleCallback callback) { + final String bundlePath, final DevSplitBundleCallback callback) { fetchSplitBundleAndCreateBundleLoader( - bundlePath, - new CallbackWithBundleLoader() { - @Override - public void onSuccess(final JSBundleLoader bundleLoader) { - mReactHost - .loadBundle(bundleLoader) - .onSuccess( - new Continuation() { - @Override - public Void then(Task task) { - if (task.getResult().equals(Boolean.TRUE)) { - String bundleURL = - getDevServerHelper().getDevServerSplitBundleURL(bundlePath); - ReactContext reactContext = mReactHost.getCurrentReactContext(); - if (reactContext != null) { - reactContext.getJSModule(HMRClient.class).registerBundle(bundleURL); - } - callback.onSuccess(); - } - return null; - } - }); - } - - @Override - public void onError(String url, Throwable cause) { - callback.onError(url, cause); - } - }); + bundlePath, + new CallbackWithBundleLoader() { + @Override + public void onSuccess(final JSBundleLoader bundleLoader) { + mReactHost + .loadBundle(bundleLoader) + .onSuccess( + new Continuation() { + @Override + public Void then(Task task) { + if (task.getResult().equals(Boolean.TRUE)) { + String bundleURL = + getDevServerHelper().getDevServerSplitBundleURL(bundlePath); + ReactContext reactContext = mReactHost.getCurrentReactContext(); + if (reactContext != null) { + reactContext.getJSModule(HMRClient.class).registerBundle(bundleURL); + } + callback.onSuccess(); + } + return null; + } + }); + } + + @Override + public void onError(String url, Throwable cause) { + callback.onError(url, cause); + } + }); } @Override @@ -102,7 +141,7 @@ public void handleReloadJS() { mReactHost.reload("BridgelessDevSupportManager.handleReloadJS()"); } - private static ReactInstanceDevHelper createInstanceDevHelper(final ReactHostImpl reactHost) { + public static ReactInstanceDevHelper createInstanceDevHelper(final ReactHostImpl reactHost) { return new ReactInstanceDevHelper() { @Override public void onReloadWithJSDebugger(JavaJSExecutor.Factory proxyExecutorFactory) { @@ -119,8 +158,8 @@ public void toggleElementInspector() { ReactContext reactContext = reactHost.getCurrentReactContext(); if (reactContext != null) { reactContext - .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) - .emit("toggleElementInspector", null); + .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) + .emit("toggleElementInspector", null); } } @@ -141,7 +180,7 @@ public View createRootView(String appKey) { Activity currentActivity = getCurrentActivity(); if (currentActivity != null && !reactHost.isSurfaceWithModuleNameAttached(appKey)) { ReactSurfaceImpl reactSurface = - ReactSurfaceImpl.createWithView(currentActivity, appKey, new Bundle()); + ReactSurfaceImpl.createWithView(currentActivity, appKey, new Bundle()); reactSurface.attach(reactHost); reactSurface.start(); diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.java index 28c266a64c4e96..87b0ced3a45f71 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostImpl.java @@ -48,7 +48,9 @@ import com.facebook.react.bridge.queue.ReactQueueConfiguration; import com.facebook.react.common.LifecycleState; import com.facebook.react.common.build.ReactBuildConfig; +import com.facebook.react.devsupport.DefaultDevSupportManagerFactory; import com.facebook.react.devsupport.DevSupportManagerBase; +import com.facebook.react.devsupport.DevSupportManagerFactory; import com.facebook.react.devsupport.InspectorFlags; import com.facebook.react.devsupport.ReleaseDevSupportManager; import com.facebook.react.devsupport.interfaces.BundleLoadCallback; @@ -101,7 +103,7 @@ public class ReactHostImpl implements ReactHost { private final Context mContext; private final ReactHostDelegate mReactHostDelegate; private final ComponentFactory mComponentFactory; - private final DevSupportManager mDevSupportManager; + private DevSupportManager mDevSupportManager; private final Executor mBGExecutor; private final Executor mUIExecutor; private final QueueThreadExceptionHandler mQueueThreadExceptionHandler; @@ -139,29 +141,49 @@ public class ReactHostImpl implements ReactHost { private @Nullable ReactHostInspectorTarget mReactHostInspectorTarget; public ReactHostImpl( - Context context, - ReactHostDelegate delegate, - ComponentFactory componentFactory, - boolean allowPackagerServerAccess, - boolean useDevSupport) { + Context context, + ReactHostDelegate delegate, + ComponentFactory componentFactory, + boolean allowPackagerServerAccess, + boolean useDevSupport) { this( - context, - delegate, - componentFactory, - Executors.newSingleThreadExecutor(), - Task.UI_THREAD_EXECUTOR, - allowPackagerServerAccess, - useDevSupport); + context, + delegate, + componentFactory, + Executors.newSingleThreadExecutor(), + Task.UI_THREAD_EXECUTOR, + allowPackagerServerAccess, + useDevSupport); } public ReactHostImpl( - Context context, - ReactHostDelegate delegate, - ComponentFactory componentFactory, - Executor bgExecutor, - Executor uiExecutor, - boolean allowPackagerServerAccess, - boolean useDevSupport) { + Context context, + ReactHostDelegate delegate, + ComponentFactory componentFactory, + Executor bgExecutor, + Executor uiExecutor, + boolean allowPackagerServerAccess, + boolean useDevSupport) { + this( + context, + delegate, + componentFactory, + bgExecutor, + uiExecutor, + allowPackagerServerAccess, + useDevSupport, + null); + } + + public ReactHostImpl( + Context context, + ReactHostDelegate delegate, + ComponentFactory componentFactory, + Executor bgExecutor, + Executor uiExecutor, + boolean allowPackagerServerAccess, + boolean useDevSupport, + @Nullable DevSupportManagerFactory devSupportManagerFactory) { mContext = context; mReactHostDelegate = delegate; mComponentFactory = componentFactory; @@ -172,13 +194,26 @@ public ReactHostImpl( mAllowPackagerServerAccess = allowPackagerServerAccess; mUseDevSupport = useDevSupport; - if (mUseDevSupport) { - mDevSupportManager = - new BridgelessDevSupportManager( - ReactHostImpl.this, mContext, mReactHostDelegate.getJsMainModulePath()); - } else { - mDevSupportManager = new ReleaseDevSupportManager(); + if (devSupportManagerFactory == null) { + devSupportManagerFactory = new DefaultDevSupportManagerFactory(); } + + mDevSupportManager = + devSupportManagerFactory.create( + /* reactHost */ ReactHostImpl.this, + /* applicationContext */ context.getApplicationContext(), + /* reactInstanceManagerHelper */ BridgelessDevSupportManager.createInstanceDevHelper( + ReactHostImpl.this), + /* packagerPathForJSBundleName */ mReactHostDelegate.getJsMainModulePath(), + /* enableOnCreate */ true, + /* redBoxHandler */ null, + /* devBundleDownloadListener */ null, + /* minNumShakes */ 2, + /* customPackagerCommandHandlers */ null, + /* surfaceDelegateFactory */ null, + /* devLoadingViewManager */ null, + /* pausedInDebuggerOverlayManager */ null, + mUseDevSupport); } @Override