Skip to content

Commit

Permalink
Register ReactInstanceManager with modern CDP backend (1/2) (facebook…
Browse files Browse the repository at this point in the history
…#43250)

Summary:
Pull Request resolved: facebook#43250

Integrates the modern CDP backend with `ReactInstanceManager` on Android.

`ReactInstanceManager` is equivalent to the CDP page / `HostTarget` concept, therefore we register the `addPage`/`removePage` calls with this object's lifecycle.

Implementation notes:
- `ReactInstanceManagerInspectorTarget` is created to avoid converting `ReactInstanceManager` to JNI (impacting tests).
- Its constructor receives a `TargetDelegate` object, so that we avoid passing the entire `ReactInstanceManager` class through (avoids cyclic dependency from `com.facebook.react.bridge` to `com.facebook.react`).

Changelog:
[Internal] - Register `ReactInstanceManager` with modern CDP backend

Reviewed By: motiz88

Differential Revision: D51456960

fbshipit-source-id: 942255bb2487fdc581eb4fa0903c8e68106f8b35
  • Loading branch information
huntie authored and facebook-github-bot committed Mar 26, 2024
1 parent 6f956af commit fcddb16
Show file tree
Hide file tree
Showing 6 changed files with 244 additions and 0 deletions.
9 changes: 9 additions & 0 deletions packages/react-native/ReactAndroid/api/ReactAndroid.api
Original file line number Diff line number Diff line change
Expand Up @@ -1152,6 +1152,15 @@ public class com/facebook/react/bridge/ReactIgnorableMountingException : java/la
public static fun isIgnorable (Ljava/lang/Throwable;)Z
}

public class com/facebook/react/bridge/ReactInstanceManagerInspectorTarget : java/lang/AutoCloseable {
public fun <init> (Lcom/facebook/react/bridge/ReactInstanceManagerInspectorTarget$TargetDelegate;)V
public fun close ()V
}

public abstract interface class com/facebook/react/bridge/ReactInstanceManagerInspectorTarget$TargetDelegate {
public abstract fun onReload ()V
}

public class com/facebook/react/bridge/ReactMarker {
public fun <init> ()V
public static fun addFabricListener (Lcom/facebook/react/bridge/ReactMarker$FabricMarkerListener;)V
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactCxxErrorHandler;
import com.facebook.react.bridge.ReactInstanceManagerInspectorTarget;
import com.facebook.react.bridge.ReactMarker;
import com.facebook.react.bridge.ReactMarkerConstants;
import com.facebook.react.bridge.ReactNoCrashSoftException;
Expand All @@ -80,6 +81,7 @@
import com.facebook.react.common.annotations.VisibleForTesting;
import com.facebook.react.config.ReactFeatureFlags;
import com.facebook.react.devsupport.DevSupportManagerFactory;
import com.facebook.react.devsupport.InspectorFlags;
import com.facebook.react.devsupport.ReactInstanceDevHelper;
import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener;
import com.facebook.react.devsupport.interfaces.DevLoadingViewManager;
Expand Down Expand Up @@ -173,6 +175,7 @@ public interface ReactInstanceEventListener
private final Context mApplicationContext;
private @Nullable @ThreadConfined(UI) DefaultHardwareBackBtnHandler mDefaultBackButtonImpl;
private @Nullable Activity mCurrentActivity;
private @Nullable ReactInstanceManagerInspectorTarget mInspectorTarget;
private final Collection<com.facebook.react.ReactInstanceEventListener>
mReactInstanceEventListeners =
Collections.synchronizedList(
Expand Down Expand Up @@ -689,6 +692,7 @@ public void onViewDetachedFromWindow(View v) {
@Deprecated
public void onHostDestroy() {
UiThreadUtil.assertOnUiThread();
destroyInspectorTarget();

if (mUseDeveloperSupport) {
mDevSupportManager.setDevSupportEnabled(false);
Expand Down Expand Up @@ -737,6 +741,7 @@ public void destroy() {
}

mHasStartedDestroying = true;
destroyInspectorTarget();

if (mUseDeveloperSupport) {
mDevSupportManager.setDevSupportEnabled(false);
Expand Down Expand Up @@ -1354,6 +1359,8 @@ private ReactApplicationContext createReactContext(

NativeModuleRegistry nativeModuleRegistry = processPackages(reactContext, mPackages);

getOrCreateInspectorTarget();

CatalystInstanceImpl.Builder catalystInstanceBuilder =
new CatalystInstanceImpl.Builder()
.setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault())
Expand Down Expand Up @@ -1476,4 +1483,27 @@ private void processPackage(
}
SystraceMessage.endSection(TRACE_TAG_REACT_JAVA_BRIDGE).flush();
}

private @Nullable ReactInstanceManagerInspectorTarget getOrCreateInspectorTarget() {
if (mInspectorTarget == null && InspectorFlags.getEnableModernCDPRegistry()) {

mInspectorTarget =
new ReactInstanceManagerInspectorTarget(
new ReactInstanceManagerInspectorTarget.TargetDelegate() {
@Override
public void onReload() {
UiThreadUtil.runOnUiThread(() -> mDevSupportManager.handleReloadJS());
}
});
}

return mInspectorTarget;
}

private void destroyInspectorTarget() {
if (mInspectorTarget != null) {
mInspectorTarget.close();
mInspectorTarget = null;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* 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.bridge;

import com.facebook.jni.HybridData;
import com.facebook.proguard.annotations.DoNotStripAny;
import java.util.concurrent.Executor;

@DoNotStripAny
public class ReactInstanceManagerInspectorTarget implements AutoCloseable {
public interface TargetDelegate {
public void onReload();
}

private final HybridData mHybridData;

public ReactInstanceManagerInspectorTarget(TargetDelegate delegate) {
mHybridData =
initHybrid(
new Executor() {
@Override
public void execute(Runnable command) {
if (UiThreadUtil.isOnUiThread()) {
command.run();
} else {
UiThreadUtil.runOnUiThread(command);
}
}
},
delegate);
}

private native HybridData initHybrid(Executor executor, TargetDelegate delegate);

public void close() {
mHybridData.resetNative();
}

static {
ReactBridge.staticInit();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "JReactMarker.h"
#include "JavaScriptExecutorHolder.h"
#include "ProxyExecutor.h"
#include "ReactInstanceManagerInspectorTarget.h"
#include "WritableNativeArray.h"
#include "WritableNativeMap.h"

Expand Down Expand Up @@ -90,6 +91,7 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
WritableNativeMap::registerNatives();
JReactMarker::registerNatives();
JInspector::registerNatives();
ReactInstanceManagerInspectorTarget::registerNatives();
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* 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.
*/

#include "ReactInstanceManagerInspectorTarget.h"

#include <fbjni/NativeRunnable.h>
#include <jsinspector-modern/InspectorFlags.h>

using namespace facebook::jni;
using namespace facebook::react::jsinspector_modern;

namespace facebook::react {

void ReactInstanceManagerInspectorTarget::TargetDelegate::onReload() const {
auto method = javaClassStatic()->getMethod<void()>("onReload");
method(self());
}

ReactInstanceManagerInspectorTarget::ReactInstanceManagerInspectorTarget(
jni::alias_ref<ReactInstanceManagerInspectorTarget::jhybridobject> jobj,
jni::alias_ref<JExecutor::javaobject> executor,
jni::alias_ref<
ReactInstanceManagerInspectorTarget::TargetDelegate::javaobject>
delegate)
: delegate_(make_global(delegate)) {
auto& inspectorFlags = InspectorFlags::getInstance();

if (inspectorFlags.getEnableModernCDPRegistry()) {
inspectorTarget_ = HostTarget::create(
*this, [javaExecutor = make_global(executor)](auto callback) mutable {
auto jrunnable =
JNativeRunnable::newObjectCxxArgs(std::move(callback));
javaExecutor->execute(jrunnable);
});

inspectorPageId_ = getInspectorInstance().addPage(
"React Native Bridge (Experimental)",
/* vm */ "",
[inspectorTarget =
inspectorTarget_](std::unique_ptr<IRemoteConnection> remote)
-> std::unique_ptr<ILocalConnection> {
return inspectorTarget->connect(
std::move(remote),
{
.integrationName =
"Android Bridge (ReactInstanceManagerInspectorTarget)",
});
},
{.nativePageReloads = true});
}
}

ReactInstanceManagerInspectorTarget::~ReactInstanceManagerInspectorTarget() {
if (inspectorPageId_.has_value()) {
getInspectorInstance().removePage(*inspectorPageId_);
}
}

jni::local_ref<ReactInstanceManagerInspectorTarget::jhybriddata>
ReactInstanceManagerInspectorTarget::initHybrid(
jni::alias_ref<jhybridobject> jobj,
jni::alias_ref<JExecutor::javaobject> executor,
jni::alias_ref<
ReactInstanceManagerInspectorTarget::TargetDelegate::javaobject>
delegate) {
return makeCxxInstance(jobj, executor, delegate);
}

void ReactInstanceManagerInspectorTarget::registerNatives() {
registerHybrid({
makeNativeMethod(
"initHybrid", ReactInstanceManagerInspectorTarget::initHybrid),
});
}

void ReactInstanceManagerInspectorTarget::onReload(
const PageReloadRequest& /*request*/) {
delegate_->onReload();
}

HostTarget* ReactInstanceManagerInspectorTarget::getInspectorTarget() {
return inspectorTarget_.get();
}

} // namespace facebook::react
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* 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.
*/

#pragma once

#include <fb/fbjni.h>
#include <jsinspector-modern/HostTarget.h>
#include <react/jni/JExecutor.h>

namespace facebook::react {

class ReactInstanceManagerInspectorTarget
: public jni::HybridClass<ReactInstanceManagerInspectorTarget>,
public jsinspector_modern::HostTargetDelegate {
private:
struct TargetDelegate : public facebook::jni::JavaClass<TargetDelegate> {
static constexpr auto kJavaDescriptor =
"Lcom/facebook/react/bridge/ReactInstanceManagerInspectorTarget$TargetDelegate;";

void onReload() const;
};

public:
static constexpr auto kJavaDescriptor =
"Lcom/facebook/react/bridge/ReactInstanceManagerInspectorTarget;";

ReactInstanceManagerInspectorTarget(
const ReactInstanceManagerInspectorTarget&) = delete;
ReactInstanceManagerInspectorTarget& operator=(
const ReactInstanceManagerInspectorTarget&) = delete;

~ReactInstanceManagerInspectorTarget() override;

static jni::local_ref<jhybriddata> initHybrid(
jni::alias_ref<jhybridobject> jobj,
jni::alias_ref<JExecutor::javaobject> executor,
jni::alias_ref<
ReactInstanceManagerInspectorTarget::TargetDelegate::javaobject>
delegate);

static void registerNatives();

void onReload(const PageReloadRequest& request) override;
jsinspector_modern::HostTarget* getInspectorTarget();

private:
friend HybridBase;

ReactInstanceManagerInspectorTarget(
jni::alias_ref<ReactInstanceManagerInspectorTarget::jhybridobject> jobj,
jni::alias_ref<JExecutor::javaobject> executor,
jni::alias_ref<
ReactInstanceManagerInspectorTarget::TargetDelegate::javaobject>
delegate);

jni::global_ref<
ReactInstanceManagerInspectorTarget::TargetDelegate::javaobject>
delegate_;
std::shared_ptr<jsinspector_modern::HostTarget> inspectorTarget_;
std::optional<int> inspectorPageId_;
};

} // namespace facebook::react

0 comments on commit fcddb16

Please sign in to comment.