Skip to content

Commit

Permalink
Move BridgelessDevSupportManager to .devsupport package (#46914)
Browse files Browse the repository at this point in the history
Summary:
Pull Request resolved: #46914

The `BridgelessDevSupportManager` should have lived inside the `.devsupport` package alongside all the other
devsupport related tooling.

It was instead created inside `.runtime` causing a tight coupling with `ReactHostImpl`.
This made it impossible for Frameworks to customize it (i.e. in Expo GO) also because there was a circular
dependency between ReactHostImpl and BridgelessDevSupportManager

In this diff I'm:
1. Breaking the circular dependency by using `ReactHostDevHelper`
2. Updating all the parameters to reference `ReactHost` rather than `ReactHostImpl`
3. Moving BridgelessDevSupportManager to the `.devsupport` package.

This is breaking for users that are manually composing a `BridgelessDevSupportManager` or
that are extending the `ReactInstanceDevHelper`.

- `ReactInstanceDevHelper` has 3 new method which will have to be implemented.
- `BridgelessDevSupportManager` is now living in a different package.

Changelog:
[Android] [Breaking] - Add 3 methods to ReactInstanceDevHelper

Reviewed By: rshest

Differential Revision: D64105790
  • Loading branch information
cortinico authored and facebook-github-bot committed Oct 16, 2024
1 parent 6b67772 commit 7a18e05
Show file tree
Hide file tree
Showing 12 changed files with 347 additions and 188 deletions.
9 changes: 8 additions & 1 deletion packages/react-native/ReactAndroid/api/ReactAndroid.api
Original file line number Diff line number Diff line change
Expand Up @@ -2134,6 +2134,7 @@ public final class com/facebook/react/devsupport/DefaultDevLoadingViewImplementa
public final class com/facebook/react/devsupport/DefaultDevSupportManagerFactory : com/facebook/react/devsupport/DevSupportManagerFactory {
public fun <init> ()V
public fun create (Landroid/content/Context;Lcom/facebook/react/devsupport/ReactInstanceDevHelper;Ljava/lang/String;ZLcom/facebook/react/devsupport/interfaces/RedBoxHandler;Lcom/facebook/react/devsupport/interfaces/DevBundleDownloadListener;ILjava/util/Map;Lcom/facebook/react/common/SurfaceDelegateFactory;Lcom/facebook/react/devsupport/interfaces/DevLoadingViewManager;Lcom/facebook/react/devsupport/interfaces/PausedInDebuggerOverlayManager;)Lcom/facebook/react/devsupport/interfaces/DevSupportManager;
public fun create (Landroid/content/Context;Lcom/facebook/react/devsupport/ReactInstanceDevHelper;Ljava/lang/String;ZLcom/facebook/react/devsupport/interfaces/RedBoxHandler;Lcom/facebook/react/devsupport/interfaces/DevBundleDownloadListener;ILjava/util/Map;Lcom/facebook/react/common/SurfaceDelegateFactory;Lcom/facebook/react/devsupport/interfaces/DevLoadingViewManager;Lcom/facebook/react/devsupport/interfaces/PausedInDebuggerOverlayManager;Z)Lcom/facebook/react/devsupport/interfaces/DevSupportManager;
}

public class com/facebook/react/devsupport/DevServerHelper {
Expand Down Expand Up @@ -2173,6 +2174,7 @@ public final class com/facebook/react/devsupport/DevSettingsActivity : android/p
}

public abstract class com/facebook/react/devsupport/DevSupportManagerBase : com/facebook/react/devsupport/interfaces/DevSupportManager {
protected final field mReactInstanceDevHelper Lcom/facebook/react/devsupport/ReactInstanceDevHelper;
public fun <init> (Landroid/content/Context;Lcom/facebook/react/devsupport/ReactInstanceDevHelper;Ljava/lang/String;ZLcom/facebook/react/devsupport/interfaces/RedBoxHandler;Lcom/facebook/react/devsupport/interfaces/DevBundleDownloadListener;ILjava/util/Map;Lcom/facebook/react/common/SurfaceDelegateFactory;Lcom/facebook/react/devsupport/interfaces/DevLoadingViewManager;Lcom/facebook/react/devsupport/interfaces/PausedInDebuggerOverlayManager;)V
public fun addCustomDevOption (Ljava/lang/String;Lcom/facebook/react/devsupport/interfaces/DevOptionHandler;)V
public fun createRootView (Ljava/lang/String;)Landroid/view/View;
Expand Down Expand Up @@ -2236,6 +2238,7 @@ public abstract interface class com/facebook/react/devsupport/DevSupportManagerB

public abstract interface class com/facebook/react/devsupport/DevSupportManagerFactory {
public abstract fun create (Landroid/content/Context;Lcom/facebook/react/devsupport/ReactInstanceDevHelper;Ljava/lang/String;ZLcom/facebook/react/devsupport/interfaces/RedBoxHandler;Lcom/facebook/react/devsupport/interfaces/DevBundleDownloadListener;ILjava/util/Map;Lcom/facebook/react/common/SurfaceDelegateFactory;Lcom/facebook/react/devsupport/interfaces/DevLoadingViewManager;Lcom/facebook/react/devsupport/interfaces/PausedInDebuggerOverlayManager;)Lcom/facebook/react/devsupport/interfaces/DevSupportManager;
public abstract fun create (Landroid/content/Context;Lcom/facebook/react/devsupport/ReactInstanceDevHelper;Ljava/lang/String;ZLcom/facebook/react/devsupport/interfaces/RedBoxHandler;Lcom/facebook/react/devsupport/interfaces/DevBundleDownloadListener;ILjava/util/Map;Lcom/facebook/react/common/SurfaceDelegateFactory;Lcom/facebook/react/devsupport/interfaces/DevLoadingViewManager;Lcom/facebook/react/devsupport/interfaces/PausedInDebuggerOverlayManager;Z)Lcom/facebook/react/devsupport/interfaces/DevSupportManager;
}

public final class com/facebook/react/devsupport/DoubleTapReloadRecognizer {
Expand Down Expand Up @@ -2327,9 +2330,12 @@ public abstract interface class com/facebook/react/devsupport/ReactInstanceDevHe
public abstract fun createRootView (Ljava/lang/String;)Landroid/view/View;
public abstract fun destroyRootView (Landroid/view/View;)V
public abstract fun getCurrentActivity ()Landroid/app/Activity;
public abstract fun getCurrentReactContext ()Lcom/facebook/react/bridge/ReactContext;
public abstract fun getJavaScriptExecutorFactory ()Lcom/facebook/react/bridge/JavaScriptExecutorFactory;
public abstract fun loadBundle (Lcom/facebook/react/bridge/JSBundleLoader;)Lcom/facebook/react/interfaces/TaskInterface;
public abstract fun onJSBundleLoadedFromServer ()V
public abstract fun onReloadWithJSDebugger (Lcom/facebook/react/bridge/JavaJSExecutor$Factory;)V
public abstract fun reload (Ljava/lang/String;)V
public abstract fun toggleElementInspector ()V
}

Expand Down Expand Up @@ -3887,6 +3893,7 @@ public abstract class com/facebook/react/runtime/JSRuntimeFactory {

public class com/facebook/react/runtime/ReactHostImpl : com/facebook/react/ReactHost {
public fun <init> (Landroid/content/Context;Lcom/facebook/react/runtime/ReactHostDelegate;Lcom/facebook/react/fabric/ComponentFactory;Ljava/util/concurrent/Executor;Ljava/util/concurrent/Executor;ZZ)V
public fun <init> (Landroid/content/Context;Lcom/facebook/react/runtime/ReactHostDelegate;Lcom/facebook/react/fabric/ComponentFactory;Ljava/util/concurrent/Executor;Ljava/util/concurrent/Executor;ZZLcom/facebook/react/devsupport/DevSupportManagerFactory;)V
public fun <init> (Landroid/content/Context;Lcom/facebook/react/runtime/ReactHostDelegate;Lcom/facebook/react/fabric/ComponentFactory;ZZ)V
public fun addBeforeDestroyListener (Lkotlin/jvm/functions/Function0;)V
public fun addReactInstanceEventListener (Lcom/facebook/react/ReactInstanceEventListener;)V
Expand Down Expand Up @@ -3918,7 +3925,7 @@ public class com/facebook/react/runtime/ReactHostImpl : com/facebook/react/React

public class com/facebook/react/runtime/ReactSurfaceImpl : com/facebook/react/interfaces/fabric/ReactSurface {
public fun <init> (Landroid/content/Context;Ljava/lang/String;Landroid/os/Bundle;)V
public fun attach (Lcom/facebook/react/runtime/ReactHostImpl;)V
public fun attach (Lcom/facebook/react/ReactHost;)V
public fun attachView (Lcom/facebook/react/runtime/ReactSurfaceView;)V
public fun clear ()V
public static fun createWithView (Landroid/content/Context;Ljava/lang/String;Landroid/os/Bundle;)Lcom/facebook/react/runtime/ReactSurfaceImpl;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
import com.facebook.react.devsupport.interfaces.PackagerStatusCallback;
import com.facebook.react.devsupport.interfaces.PausedInDebuggerOverlayManager;
import com.facebook.react.devsupport.interfaces.RedBoxHandler;
import com.facebook.react.interfaces.TaskInterface;
import com.facebook.react.internal.AndroidChoreographerProvider;
import com.facebook.react.internal.ChoreographerProvider;
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags;
Expand Down Expand Up @@ -369,6 +370,23 @@ public void destroyRootView(View rootView) {
((ReactRootView) rootView).unmountReactApplication();
}
}

@Override
public void reload(String s) {
// no-op not implemented for Bridge Mode
}

@Override
public TaskInterface<Boolean> loadBundle(JSBundleLoader bundleLoader) {
// no-op not implemented for Bridge Mode
return null;
}

@Override
public ReactContext getCurrentReactContext() {
// no-op not implemented for Bridge Mode
return null;
}
};
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

package com.facebook.react.devsupport;

import android.content.Context;
import androidx.annotation.Nullable;
import com.facebook.infer.annotation.Nullsafe;
import com.facebook.react.bridge.JSBundleLoader;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.common.SurfaceDelegateFactory;
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.packagerconnection.RequestHandler;
import java.util.Map;

/**
* An implementation of {@link com.facebook.react.devsupport.interfaces.DevSupportManager} that
* extends the functionality in {@link DevSupportManagerBase} with some additional, more flexible
* APIs for asynchronously loading the JS bundle.
*/
@Nullsafe(Nullsafe.Mode.LOCAL)
class BridgelessDevSupportManager extends DevSupportManagerBase {

public BridgelessDevSupportManager(
Context context,
ReactInstanceDevHelper reactInstanceManagerHelper,
@Nullable String packagerPathForJSBundleName) {
this(
context.getApplicationContext(),
reactInstanceManagerHelper,
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(
Context applicationContext,
ReactInstanceDevHelper reactInstanceManagerHelper,
@Nullable String packagerPathForJSBundleName,
boolean enableOnCreate,
@Nullable RedBoxHandler redBoxHandler,
@Nullable DevBundleDownloadListener devBundleDownloadListener,
int minNumShakes,
@Nullable Map<String, RequestHandler> customPackagerCommandHandlers,
@Nullable SurfaceDelegateFactory surfaceDelegateFactory,
@Nullable DevLoadingViewManager devLoadingViewManager,
@Nullable PausedInDebuggerOverlayManager pausedInDebuggerOverlayManager) {
super(
applicationContext,
reactInstanceManagerHelper,
packagerPathForJSBundleName,
enableOnCreate,
redBoxHandler,
devBundleDownloadListener,
minNumShakes,
customPackagerCommandHandlers,
surfaceDelegateFactory,
devLoadingViewManager,
pausedInDebuggerOverlayManager);
}

@Override
protected String getUniqueTag() {
return "Bridgeless";
}

@Override
public void loadSplitBundleFromServer(
final String bundlePath, final DevSplitBundleCallback callback) {
fetchSplitBundleAndCreateBundleLoader(
bundlePath,
new CallbackWithBundleLoader() {
@Override
public void onSuccess(final JSBundleLoader bundleLoader) {
try {
mReactInstanceDevHelper.loadBundle(bundleLoader).waitForCompletion();
String bundleURL = getDevServerHelper().getDevServerSplitBundleURL(bundlePath);
ReactContext reactContext = mReactInstanceDevHelper.getCurrentReactContext();
if (reactContext != null) {
reactContext.getJSModule(HMRClient.class).registerBundle(bundleURL);
}
callback.onSuccess();
} catch (InterruptedException e) {
throw new RuntimeException(
"[BridgelessDevSupportManager]: Got interrupted while loading bundle", e);
}
}

@Override
public void onError(String url, Throwable cause) {
callback.onError(url, cause);
}
});
}

@Override
public void handleReloadJS() {
UiThreadUtil.assertOnUiThread();

// dismiss redbox if exists
hideRedboxDialog();
mReactInstanceDevHelper.reload("BridgelessDevSupportManager.handleReloadJS()");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ import com.facebook.react.packagerconnection.RequestHandler
/**
* A simple factory that creates instances of [DevSupportManager] implementations. Uses reflection
* to create BridgeDevSupportManager if it exists. This allows ProGuard to strip that class and its
* dependencies in release builds. If the class isn't found, [ ] is returned instead.
* dependencies in release builds. If the class isn't found, [PerftestDevSupportManager] is returned
* instead.
*/
public class DefaultDevSupportManagerFactory : DevSupportManagerFactory {

Expand Down Expand Up @@ -85,6 +86,37 @@ public class DefaultDevSupportManagerFactory : DevSupportManagerFactory {
}
}

override fun create(
applicationContext: Context,
reactInstanceManagerHelper: ReactInstanceDevHelper,
packagerPathForJSBundleName: String?,
enableOnCreate: Boolean,
redBoxHandler: RedBoxHandler?,
devBundleDownloadListener: DevBundleDownloadListener?,
minNumShakes: Int,
customPackagerCommandHandlers: MutableMap<String, RequestHandler>?,
surfaceDelegateFactory: SurfaceDelegateFactory?,
devLoadingViewManager: DevLoadingViewManager?,
pausedInDebuggerOverlayManager: PausedInDebuggerOverlayManager?,
useDevSupport: Boolean
): DevSupportManager =
if (!useDevSupport) {
ReleaseDevSupportManager()
} else {
BridgelessDevSupportManager(
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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public interface CallbackWithBundleLoader {
private final BroadcastReceiver mReloadAppBroadcastReceiver;
private final DevServerHelper mDevServerHelper;
private final LinkedHashMap<String, DevOptionHandler> mCustomDevOptions = new LinkedHashMap<>();
private final ReactInstanceDevHelper mReactInstanceDevHelper;
protected final ReactInstanceDevHelper mReactInstanceDevHelper;
private final @Nullable String mJSAppBundleName;
private final File mJSBundleDownloadedFile;
private final File mJSSplitBundlesDir;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
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,
Expand All @@ -31,4 +35,22 @@ DevSupportManager create(
@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 BridgelessDevSupportManager}
*/
DevSupportManager create(
Context applicationContext,
ReactInstanceDevHelper reactInstanceManagerHelper,
@Nullable String packagerPathForJSBundleName,
boolean enableOnCreate,
@Nullable RedBoxHandler redBoxHandler,
@Nullable DevBundleDownloadListener devBundleDownloadListener,
int minNumShakes,
@Nullable Map<String, RequestHandler> customPackagerCommandHandlers,
@Nullable SurfaceDelegateFactory surfaceDelegateFactory,
@Nullable DevLoadingViewManager devLoadingViewManager,
@Nullable PausedInDebuggerOverlayManager pausedInDebuggerOverlayManager,
boolean useDevSupport);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@
import android.app.Activity;
import android.view.View;
import androidx.annotation.Nullable;
import com.facebook.react.bridge.JSBundleLoader;
import com.facebook.react.bridge.JavaJSExecutor;
import com.facebook.react.bridge.JavaScriptExecutorFactory;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.interfaces.TaskInterface;

/**
* Interface used by {@link DevSupportManager} for accessing some fields and methods of {@link
* ReactInstanceManager} or {@link ReactHostImpl} for the purpose of displaying and handling
* developer menu options.
* ReactInstanceManager} or {@link ReactHost} for the purpose of displaying and handling developer
* menu options.
*/
public interface ReactInstanceDevHelper {

Expand All @@ -39,4 +42,11 @@ public interface ReactInstanceDevHelper {
View createRootView(String appKey);

void destroyRootView(View rootView);

void reload(String s);

TaskInterface<Boolean> loadBundle(JSBundleLoader bundleLoader);

@Nullable
ReactContext getCurrentReactContext();
}
Loading

0 comments on commit 7a18e05

Please sign in to comment.