Skip to content

Commit

Permalink
Push ReactContext logic in derived classes (#44026)
Browse files Browse the repository at this point in the history
Summary:
Pull Request resolved: #44026

Changelog: [Android][Removed] Delete ReactContext.initializeWithInstance(). ReactContext now no longer contains legacy react instance methods. Please use BridgeReactInstance instead.

Yet another attempt to land this (last one was D55505416).

Copy-pasting below the amazing summary from RSNara.

## Context
Prior, ReactContext used to implement bridge logic.

For bridgeless mode, we created BridgelessReactContext < ReactContext

## Problem

This could lead to failures: we could call bridge methods in bridgeless mode.

## Changes
Primary change:
- Make all the react instance methods inside ReactContext abstract.

Secondary changes: Implement react instance methods in concrete subclasses:
- **New:** BridgeReactContext: By delegating to CatalystInstance
- **New:** ThemedReactContext: By delegating to inner ReactContext
- **Unchanged:** BridgelessReactContext: By delegating to ReactHost

## Auxiliary changes
This fixes ThemedReactContext in bridgeless mode.

**Problem:** Prior, ThemedReactContext's react instance methods did not work in bridgeless mode: ThemedReactContext wasn't initialized in bridgeless mode, so all those methods had undefined behaviour.

**Solution:** ThemedReactContext now implements all react instance methods, by just forwarding to the initialized ReactContext it decorates (which has an instance).

NOTE: Intentionally not converting `BridgeReactContext` to Kotlin to minimize the risk of these changes.

Reviewed By: RSNara

Differential Revision: D55964787

fbshipit-source-id: b404efe0c7095894fa815165cc8682f78dccfa17
  • Loading branch information
fabriziocucci authored and facebook-github-bot committed Apr 12, 2024
1 parent b4f9aa1 commit 05ef779
Show file tree
Hide file tree
Showing 6 changed files with 349 additions and 200 deletions.
69 changes: 47 additions & 22 deletions packages/react-native/ReactAndroid/api/ReactAndroid.api
Original file line number Diff line number Diff line change
Expand Up @@ -543,8 +543,25 @@ public abstract class com/facebook/react/bridge/BaseJavaModule : com/facebook/re
public fun invalidate ()V
}

public final class com/facebook/react/bridge/BridgeReactContext : com/facebook/react/bridge/ReactApplicationContext {
public class com/facebook/react/bridge/BridgeReactContext : com/facebook/react/bridge/ReactApplicationContext {
public fun <init> (Landroid/content/Context;)V
public fun destroy ()V
public fun getCatalystInstance ()Lcom/facebook/react/bridge/CatalystInstance;
public fun getFabricUIManager ()Lcom/facebook/react/bridge/UIManager;
public fun getJSModule (Ljava/lang/Class;)Lcom/facebook/react/bridge/JavaScriptModule;
public fun getJavaScriptContextHolder ()Lcom/facebook/react/bridge/JavaScriptContextHolder;
public fun getNativeModule (Ljava/lang/Class;)Lcom/facebook/react/bridge/NativeModule;
public fun getNativeModules ()Ljava/util/Collection;
public fun getRuntimeExecutor ()Lcom/facebook/react/bridge/RuntimeExecutor;
public fun getSourceURL ()Ljava/lang/String;
public fun handleException (Ljava/lang/Exception;)V
public fun hasActiveCatalystInstance ()Z
public fun hasActiveReactInstance ()Z
public fun hasCatalystInstance ()Z
public fun hasNativeModule (Ljava/lang/Class;)Z
public fun initializeWithInstance (Lcom/facebook/react/bridge/CatalystInstance;)V
public fun isBridgeless ()Z
public fun registerSegment (ILjava/lang/String;Lcom/facebook/react/bridge/Callback;)V
}

public abstract interface class com/facebook/react/bridge/Callback {
Expand Down Expand Up @@ -1090,36 +1107,35 @@ public abstract class com/facebook/react/bridge/ReactContext : android/content/C
public fun assertOnNativeModulesQueueThread ()V
public fun assertOnNativeModulesQueueThread (Ljava/lang/String;)V
public fun assertOnUiQueueThread ()V
public fun destroy ()V
public abstract fun destroy ()V
public fun emitDeviceEvent (Ljava/lang/String;)V
public fun emitDeviceEvent (Ljava/lang/String;Ljava/lang/Object;)V
public fun getCatalystInstance ()Lcom/facebook/react/bridge/CatalystInstance;
public abstract fun getCatalystInstance ()Lcom/facebook/react/bridge/CatalystInstance;
public fun getCurrentActivity ()Landroid/app/Activity;
public fun getExceptionHandler ()Lcom/facebook/react/bridge/JSExceptionHandler;
public fun getFabricUIManager ()Lcom/facebook/react/bridge/UIManager;
public abstract fun getFabricUIManager ()Lcom/facebook/react/bridge/UIManager;
public fun getJSExceptionHandler ()Lcom/facebook/react/bridge/JSExceptionHandler;
public fun getJSMessageQueueThread ()Lcom/facebook/react/bridge/queue/MessageQueueThread;
public fun getJSModule (Ljava/lang/Class;)Lcom/facebook/react/bridge/JavaScriptModule;
public fun getJavaScriptContextHolder ()Lcom/facebook/react/bridge/JavaScriptContextHolder;
public abstract fun getJSModule (Ljava/lang/Class;)Lcom/facebook/react/bridge/JavaScriptModule;
public abstract fun getJavaScriptContextHolder ()Lcom/facebook/react/bridge/JavaScriptContextHolder;
public fun getLifecycleState ()Lcom/facebook/react/common/LifecycleState;
public fun getNativeModule (Ljava/lang/Class;)Lcom/facebook/react/bridge/NativeModule;
public fun getNativeModules ()Ljava/util/Collection;
public abstract fun getNativeModule (Ljava/lang/Class;)Lcom/facebook/react/bridge/NativeModule;
public abstract fun getNativeModules ()Ljava/util/Collection;
public fun getNativeModulesMessageQueueThread ()Lcom/facebook/react/bridge/queue/MessageQueueThread;
public fun getSourceURL ()Ljava/lang/String;
public abstract fun getSourceURL ()Ljava/lang/String;
public fun getSystemService (Ljava/lang/String;)Ljava/lang/Object;
public fun getUiMessageQueueThread ()Lcom/facebook/react/bridge/queue/MessageQueueThread;
public fun handleException (Ljava/lang/Exception;)V
public fun hasActiveCatalystInstance ()Z
public fun hasActiveReactInstance ()Z
public fun hasCatalystInstance ()Z
public abstract fun handleException (Ljava/lang/Exception;)V
public abstract fun hasActiveCatalystInstance ()Z
public abstract fun hasActiveReactInstance ()Z
public abstract fun hasCatalystInstance ()Z
public fun hasCurrentActivity ()Z
public fun hasNativeModule (Ljava/lang/Class;)Z
public abstract fun hasNativeModule (Ljava/lang/Class;)Z
protected fun initializeFromOther (Lcom/facebook/react/bridge/ReactContext;)V
protected fun initializeInteropModules ()V
protected fun initializeInteropModules (Lcom/facebook/react/bridge/ReactContext;)V
public fun initializeMessageQueueThreads (Lcom/facebook/react/bridge/queue/ReactQueueConfiguration;)V
public fun initializeWithInstance (Lcom/facebook/react/bridge/CatalystInstance;)V
public fun internal_registerInteropModule (Ljava/lang/Class;Ljava/lang/Object;)V
public fun isBridgeless ()Z
public abstract fun isBridgeless ()Z
public fun isOnJSQueueThread ()Z
public fun isOnNativeModulesQueueThread ()Z
public fun isOnUiQueueThread ()Z
Expand All @@ -1129,7 +1145,7 @@ public abstract class com/facebook/react/bridge/ReactContext : android/content/C
public fun onHostResume (Landroid/app/Activity;)V
public fun onNewIntent (Landroid/app/Activity;Landroid/content/Intent;)V
public fun onWindowFocusChange (Z)V
public fun registerSegment (ILjava/lang/String;Lcom/facebook/react/bridge/Callback;)V
public abstract fun registerSegment (ILjava/lang/String;Lcom/facebook/react/bridge/Callback;)V
public fun removeActivityEventListener (Lcom/facebook/react/bridge/ActivityEventListener;)V
public fun removeLifecycleEventListener (Lcom/facebook/react/bridge/LifecycleEventListener;)V
public fun removeWindowFocusChangeListener (Lcom/facebook/react/bridge/WindowFocusChangeListener;)V
Expand Down Expand Up @@ -4894,16 +4910,25 @@ public class com/facebook/react/uimanager/ThemedReactContext : com/facebook/reac
public fun <init> (Lcom/facebook/react/bridge/ReactApplicationContext;Landroid/content/Context;)V
public fun <init> (Lcom/facebook/react/bridge/ReactApplicationContext;Landroid/content/Context;Ljava/lang/String;)V
public fun <init> (Lcom/facebook/react/bridge/ReactApplicationContext;Landroid/content/Context;Ljava/lang/String;I)V
public fun addLifecycleEventListener (Lcom/facebook/react/bridge/LifecycleEventListener;)V
public fun getCurrentActivity ()Landroid/app/Activity;
public fun destroy ()V
public fun getCatalystInstance ()Lcom/facebook/react/bridge/CatalystInstance;
public fun getFabricUIManager ()Lcom/facebook/react/bridge/UIManager;
public fun getJSModule (Ljava/lang/Class;)Lcom/facebook/react/bridge/JavaScriptModule;
public fun getJavaScriptContextHolder ()Lcom/facebook/react/bridge/JavaScriptContextHolder;
public fun getModuleName ()Ljava/lang/String;
public fun getNativeModule (Ljava/lang/Class;)Lcom/facebook/react/bridge/NativeModule;
public fun getNativeModules ()Ljava/util/Collection;
public fun getReactApplicationContext ()Lcom/facebook/react/bridge/ReactApplicationContext;
public fun getSourceURL ()Ljava/lang/String;
public fun getSurfaceID ()Ljava/lang/String;
public fun getSurfaceId ()I
public fun hasCurrentActivity ()Z
public fun handleException (Ljava/lang/Exception;)V
public fun hasActiveCatalystInstance ()Z
public fun hasActiveReactInstance ()Z
public fun hasCatalystInstance ()Z
public fun hasNativeModule (Ljava/lang/Class;)Z
public fun isBridgeless ()Z
public fun removeLifecycleEventListener (Lcom/facebook/react/bridge/LifecycleEventListener;)V
public fun registerSegment (ILjava/lang/String;Lcom/facebook/react/bridge/Callback;)V
}

public class com/facebook/react/uimanager/TouchTargetHelper {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
/*
* 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 android.content.Context;
import androidx.annotation.Nullable;
import com.facebook.common.logging.FLog;
import com.facebook.infer.annotation.Assertions;
import com.facebook.infer.annotation.Nullsafe;
import com.facebook.react.bridge.queue.ReactQueueConfiguration;
import com.facebook.react.common.ReactConstants;
import com.facebook.react.common.annotations.DeprecatedInNewArchitecture;
import java.util.Collection;

/**
* This is the bridge-specific concrete subclass of ReactContext. ReactContext has many methods that
* delegate to the react instance. This subclass implements those methods, by delegating to the
* CatalystInstance. If you need to create a ReactContext within an "bridge context", please create
* BridgeReactContext.
*/
@DeprecatedInNewArchitecture
@Nullsafe(Nullsafe.Mode.LOCAL)
public class BridgeReactContext extends ReactApplicationContext {

private static final String TAG = "BridgeReactContext";

private static final String EARLY_CALL_EXCEPTION_MESSAGE =
"Called %s before setting the Catalyst instance!";

private static final String LATE_CALL_EXCEPTION_MESSAGE =
"Called %s after destroying the Catalyst instance!";

private volatile boolean mDestroyed = false;
private @Nullable CatalystInstance mCatalystInstance;

public BridgeReactContext(Context context) {
super(context);
}

public void initializeWithInstance(CatalystInstance catalystInstance) {
if (catalystInstance == null) {
throw new IllegalArgumentException("CatalystInstance cannot be null.");
}
if (mCatalystInstance != null) {
throw new IllegalStateException("ReactContext has been already initialized");
}
if (mDestroyed) {
ReactSoftExceptionLogger.logSoftException(
TAG,
new IllegalStateException("Cannot initialize ReactContext after it has been destroyed."));
}

mCatalystInstance = catalystInstance;

ReactQueueConfiguration queueConfig = catalystInstance.getReactQueueConfiguration();
initializeMessageQueueThreads(queueConfig);
initializeInteropModules();
}

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

mDestroyed = true;
if (mCatalystInstance != null) {
mCatalystInstance.destroy();
}
}

@Override
public CatalystInstance getCatalystInstance() {
return Assertions.assertNotNull(mCatalystInstance);
}

@Override
public @Nullable UIManager getFabricUIManager() {
CatalystInstance catalystInstance = getNonNullableCatalystInstanceOrThrow("getFabricUIManager");
UIManager uiManager = catalystInstance.getFabricUIManager();
return uiManager != null
? uiManager
: (UIManager) catalystInstance.getJSIModule(JSIModuleType.UIManager);
}

@Override
public @Nullable JavaScriptContextHolder getJavaScriptContextHolder() {
return mCatalystInstance != null ? mCatalystInstance.getJavaScriptContextHolder() : null;
}

@Override
public <T extends JavaScriptModule> T getJSModule(Class<T> jsInterface) {
CatalystInstance catalystInstance = getNonNullableCatalystInstanceOrThrow("getJSModule");

if (mInteropModuleRegistry != null
&& mInteropModuleRegistry.shouldReturnInteropModule(jsInterface)) {
return mInteropModuleRegistry.getInteropModule(jsInterface);
}

return catalystInstance.getJSModule(jsInterface);
}

@Override
public @Nullable <T extends NativeModule> T getNativeModule(Class<T> nativeModuleInterface) {
CatalystInstance catalystInstance = getNonNullableCatalystInstanceOrThrow("getNativeModule");
return catalystInstance.getNativeModule(nativeModuleInterface);
}

@Override
public Collection<NativeModule> getNativeModules() {
CatalystInstance catalystInstance = getNonNullableCatalystInstanceOrThrow("getNativeModules");
return catalystInstance.getNativeModules();
}

@Override
public @Nullable RuntimeExecutor getRuntimeExecutor() {
CatalystInstance catalystInstance = getNonNullableCatalystInstanceOrThrow("getRuntimeExecutor");
return catalystInstance.getRuntimeExecutor();
}

@Override
public @Nullable String getSourceURL() {
return mCatalystInstance != null ? mCatalystInstance.getSourceURL() : null;
}

@Override
public void handleException(Exception e) {
JSExceptionHandler jsExceptionHandler = getJSExceptionHandler();
if (hasActiveReactInstance() && jsExceptionHandler != null) {
jsExceptionHandler.handleException(e);
} else {
FLog.e(
ReactConstants.TAG,
"Unable to handle Exception - catalystInstanceVariableExists: "
+ (mCatalystInstance != null)
+ " - isCatalystInstanceAlive: "
+ hasActiveReactInstance()
+ " - hasExceptionHandler: "
+ (jsExceptionHandler != null),
e);
throw new IllegalStateException(e);
}
}

@Deprecated
@Override
public boolean hasActiveCatalystInstance() {
return hasActiveReactInstance();
}

@Override
public boolean hasActiveReactInstance() {
return mCatalystInstance != null && !mCatalystInstance.isDestroyed();
}

@Override
public boolean hasCatalystInstance() {
return mCatalystInstance != null;
}

@Override
public <T extends NativeModule> boolean hasNativeModule(Class<T> nativeModuleInterface) {
CatalystInstance catalystInstance = getNonNullableCatalystInstanceOrThrow("hasNativeModule");
return catalystInstance.hasNativeModule(nativeModuleInterface);
}

@Override
public boolean isBridgeless() {
return false;
}

@Override
public void registerSegment(int segmentId, String path, Callback callback) {
Assertions.assertNotNull(mCatalystInstance).registerSegment(segmentId, path);
Assertions.assertNotNull(callback).invoke();
}

private CatalystInstance getNonNullableCatalystInstanceOrThrow(String accessTarget) {
if (mCatalystInstance != null) {
return mCatalystInstance;
}
throw new IllegalStateException(
String.format(
mDestroyed ? LATE_CALL_EXCEPTION_MESSAGE : EARLY_CALL_EXCEPTION_MESSAGE, accessTarget));
}
}

This file was deleted.

Loading

0 comments on commit 05ef779

Please sign in to comment.