Skip to content

Commit

Permalink
[0.73] Collection of Android picks for RC4 (#41221)
Browse files Browse the repository at this point in the history
Co-authored-by: Lulu Wu <luluwu@meta.com>
Co-authored-by: Gabriel Donadel <donadeldev@gmail.com>
fix `java.lang.NoSuchMethodError` for Bridgeless (#41081)
resolved: #41081
resolved: #40999
resolved: #41165
Fix RNTester not showing Redbox when Metro is not connected (#41191)
resolved: #41191
resolved: #41190
resolved: #41085
resolved: #41206
  • Loading branch information
3 people authored Nov 2, 2023
1 parent 2664b8b commit c8ad23a
Show file tree
Hide file tree
Showing 11 changed files with 177 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import com.facebook.react.tasks.GenerateCodegenSchemaTask
import com.facebook.react.utils.AgpConfiguratorUtils.configureBuildConfigFieldsForApp
import com.facebook.react.utils.AgpConfiguratorUtils.configureBuildConfigFieldsForLibraries
import com.facebook.react.utils.AgpConfiguratorUtils.configureDevPorts
import com.facebook.react.utils.AgpConfiguratorUtils.configureNamespaceForLibraries
import com.facebook.react.utils.BackwardCompatUtils.configureBackwardCompatibilityReactMap
import com.facebook.react.utils.DependencyUtils.configureDependencies
import com.facebook.react.utils.DependencyUtils.configureRepositories
Expand Down Expand Up @@ -80,6 +81,7 @@ class ReactPlugin : Plugin<Project> {

// Library Only Configuration
configureBuildConfigFieldsForLibraries(project)
configureNamespaceForLibraries(project)
project.pluginManager.withPlugin("com.android.library") {
configureCodegen(project, extension, rootExtension, isLibrary = true)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,17 @@
package com.facebook.react.utils

import com.android.build.api.variant.AndroidComponentsExtension
import com.android.build.gradle.LibraryExtension
import com.facebook.react.ReactExtension
import com.facebook.react.utils.ProjectUtils.isHermesEnabled
import com.facebook.react.utils.ProjectUtils.isNewArchEnabled
import java.io.File
import javax.xml.parsers.DocumentBuilder
import javax.xml.parsers.DocumentBuilderFactory
import org.gradle.api.Action
import org.gradle.api.Project
import org.gradle.api.plugins.AppliedPlugin
import org.w3c.dom.Element

@Suppress("UnstableApiUsage")
internal object AgpConfiguratorUtils {
Expand Down Expand Up @@ -63,6 +68,43 @@ internal object AgpConfiguratorUtils {
project.pluginManager.withPlugin("com.android.application", action)
project.pluginManager.withPlugin("com.android.library", action)
}

fun configureNamespaceForLibraries(appProject: Project) {
appProject.rootProject.allprojects { subproject ->
subproject.pluginManager.withPlugin("com.android.library") {
subproject.extensions.getByType(AndroidComponentsExtension::class.java).finalizeDsl { ext ->
if (ext.namespace == null) {
val android = subproject.extensions.getByType(LibraryExtension::class.java)
val manifestFile = android.sourceSets.getByName("main").manifest.srcFile

manifestFile
.takeIf { it.exists() }
?.let { file ->
getPackageNameFromManifest(file)?.let { packageName ->
ext.namespace = packageName
}
}
}
}
}
}
}
}

const val DEFAULT_DEV_SERVER_PORT = "8081"

fun getPackageNameFromManifest(manifest: File): String? {
val factory: DocumentBuilderFactory = DocumentBuilderFactory.newInstance()
val builder: DocumentBuilder = factory.newDocumentBuilder()

try {
val xmlDocument = builder.parse(manifest)

val manifestElement = xmlDocument.getElementsByTagName("manifest").item(0) as? Element
val packageName = manifestElement?.getAttribute("package")

return if (packageName.isNullOrEmpty()) null else packageName
} catch (e: Exception) {
return null
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* 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.utils

import java.io.File
import org.junit.Assert.*
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder

class AgpConfiguratorUtilsTest {

@get:Rule val tempFolder = TemporaryFolder()

@Test
fun getPackageNameFromManifest_withEmptyFile_returnsNull() {
val mainFolder = tempFolder.newFolder("awesome-module/src/main/")
val manifest = File(mainFolder, "AndroidManifest.xml").apply { writeText("") }

val actual = getPackageNameFromManifest(manifest)
assertNull(actual)
}

@Test
fun getPackageNameFromManifest_withMissingPackage_returnsNull() {
val mainFolder = tempFolder.newFolder("awesome-module/src/main/")
val manifest =
File(mainFolder, "AndroidManifest.xml").apply {
writeText(
// language=xml
"""
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>
"""
.trimIndent())
}

val actual = getPackageNameFromManifest(manifest)
assertNull(actual)
}

@Test
fun getPackageNameFromManifest_withPackage_returnsPackage() {
val mainFolder = tempFolder.newFolder("awesome-module/src/main/")
val manifest =
File(mainFolder, "AndroidManifest.xml").apply {
writeText(
// language=xml
"""
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.facebook.react" >
</manifest>
"""
.trimIndent())
}

val actual = getPackageNameFromManifest(manifest)
assertNotNull(actual)
assertEquals("com.facebook.react", actual)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,6 @@ public BridgeDevSupportManager(
surfaceDelegateFactory,
devLoadingViewManager);

mReactInstanceManagerHelper = reactInstanceManagerHelper;
mDevLoadingViewManager = devLoadingViewManager;

if (getDevSettings().isStartSamplingProfilerOnInit()) {
// Only start the profiler. If its already running, there is an error
if (!mIsSamplingProfilerEnabled) {
Expand All @@ -112,14 +109,6 @@ public void onOptionSelected() {
});
}

public DevLoadingViewManager getDevLoadingViewManager() {
return mDevLoadingViewManager;
}

public ReactInstanceDevHelper getReactInstanceManagerHelper() {
return mReactInstanceManagerHelper;
}

@Override
protected String getUniqueTag() {
return "Bridge";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -695,7 +695,11 @@ public DevServerHelper getDevServerHelper() {
return mDevServerHelper;
}

protected ReactInstanceDevHelper getReactInstanceDevHelper() {
public DevLoadingViewManager getDevLoadingViewManager() {
return mDevLoadingViewManager;
}

public ReactInstanceDevHelper getReactInstanceDevHelper() {
return mReactInstanceDevHelper;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.devsupport.BridgeDevSupportManager;
import com.facebook.react.devsupport.DefaultDevLoadingViewImplementation;
import com.facebook.react.devsupport.DevSupportManagerBase;
import com.facebook.react.devsupport.interfaces.DevLoadingViewManager;
import com.facebook.react.module.annotations.ReactModule;

Expand All @@ -28,14 +27,9 @@ public class DevLoadingModule extends NativeDevLoadingViewSpec {
public DevLoadingModule(ReactApplicationContext reactContext) {
super(reactContext);
mJSExceptionHandler = reactContext.getJSExceptionHandler();
if (mJSExceptionHandler != null && mJSExceptionHandler instanceof BridgeDevSupportManager) {
if (mJSExceptionHandler != null && mJSExceptionHandler instanceof DevSupportManagerBase) {
mDevLoadingViewManager =
((BridgeDevSupportManager) mJSExceptionHandler).getDevLoadingViewManager();
mDevLoadingViewManager =
mDevLoadingViewManager != null
? mDevLoadingViewManager
: new DefaultDevLoadingViewImplementation(
((BridgeDevSupportManager) mJSExceptionHandler).getReactInstanceManagerHelper());
((DevSupportManagerBase) mJSExceptionHandler).getDevLoadingViewManager();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@

import com.facebook.infer.annotation.Nullsafe;
import com.facebook.jni.HybridData;
import com.facebook.jni.annotations.DoNotStrip;
import com.facebook.jni.annotations.DoNotStripAny;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableNativeArray;
import com.facebook.react.modules.core.JavaScriptTimerExecutor;
import com.facebook.soloader.SoLoader;

@Nullsafe(Nullsafe.Mode.LOCAL)
@DoNotStripAny
class JSTimerExecutor implements JavaScriptTimerExecutor {

static {
Expand All @@ -24,6 +26,7 @@ class JSTimerExecutor implements JavaScriptTimerExecutor {

@DoNotStrip private final HybridData mHybridData;

@DoNotStrip
public JSTimerExecutor(HybridData hybridData) {
mHybridData = hybridData;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,9 @@ DefaultHardwareBackBtnHandler getDefaultBackButtonHandler() {
final String method = "handleHostException(message = \"" + e.getMessage() + "\")";
log(method);

if (DEV) {
mDevSupportManager.handleException(e);
}
destroy(method, e);
mReactHostDelegate.handleInstanceException(e);
}
Expand Down Expand Up @@ -922,6 +925,7 @@ private Task<ReactInstance> newGetOrCreateReactInstanceTask() {
final JSBundleLoader bundleLoader = task.getResult();
final BridgelessReactContext reactContext = getOrCreateReactContext();
final DevSupportManager devSupportManager = getDevSupportManager();
reactContext.setJSExceptionHandler(devSupportManager);

log(method, "Creating ReactInstance");
final ReactInstance instance =
Expand Down Expand Up @@ -1036,6 +1040,7 @@ private Task<ReactInstance> oldGetOrCreateReactInstanceTask() {

final BridgelessReactContext reactContext = getOrCreateReactContext();
final DevSupportManager devSupportManager = getDevSupportManager();
reactContext.setJSExceptionHandler(devSupportManager);

return getJsBundleLoader()
.onSuccess(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.Nullable;

/**
Expand All @@ -93,6 +94,7 @@ final class ReactInstance {
private final TurboModuleManager mTurboModuleManager;
private final FabricUIManager mFabricUIManager;
private final JavaTimerManager mJavaTimerManager;
private final Map<String, ViewManager> mViewManagers = new ConcurrentHashMap<>();

@DoNotStrip @Nullable private ComponentNameResolverManager mComponentNameResolverManager;
@DoNotStrip @Nullable private UIConstantsProviderManager mUIConstantsProviderManager;
Expand Down Expand Up @@ -489,8 +491,12 @@ public void registerSegment(int segmentId, String path) {
}

private @Nullable ViewManager createViewManager(String viewManagerName) {
// Return cached view manager if available, no matter it's eagerly or lazily loaded
if (mViewManagers.containsKey(viewManagerName)) {
return mViewManagers.get(viewManagerName);
}
List<ReactPackage> packages = mReactPackages;
if (mDelegate != null) {
List<ReactPackage> packages = mReactPackages;
if (packages != null) {
synchronized (packages) {
for (ReactPackage reactPackage : packages) {
Expand All @@ -499,6 +505,7 @@ public void registerSegment(int segmentId, String path) {
((ViewManagerOnDemandReactPackage) reactPackage)
.createViewManager(mBridgelessReactContext, viewManagerName);
if (viewManager != null) {
mViewManagers.put(viewManagerName, viewManager);
return viewManager;
}
}
Expand All @@ -507,7 +514,17 @@ public void registerSegment(int segmentId, String path) {
}
}

return null;
// Once a view manager is not found in all react packages via lazy loading, fall back to default
// implementation: eagerly initialize all view managers
for (ReactPackage reactPackage : packages) {
List<ViewManager> viewManagersInPackage =
reactPackage.createViewManagers(mBridgelessReactContext);
for (ViewManager viewManager : viewManagersInPackage) {
mViewManagers.put(viewManager.getName(), viewManager);
}
}

return mViewManagers.get(viewManagerName);
}

private @NonNull Collection<String> getViewManagerNames() {
Expand All @@ -534,8 +551,28 @@ public void registerSegment(int segmentId, String path) {

private @NonNull NativeMap getUIManagerConstants() {
List<ViewManager> viewManagers = new ArrayList<ViewManager>();
for (String viewManagerName : getViewManagerNames()) {
viewManagers.add(createViewManager(viewManagerName));
boolean canLoadViewManagersLazily = true;

List<ReactPackage> packages = mReactPackages;
for (ReactPackage reactPackage : packages) {
if (!(reactPackage instanceof ViewManagerOnDemandReactPackage)) {
canLoadViewManagersLazily = false;
break;
}
}
// 1, Retrive view managers via on demand loading
if (canLoadViewManagersLazily) {
for (String viewManagerName : getViewManagerNames()) {
viewManagers.add(createViewManager(viewManagerName));
}
} else {
// 2, There are packages that don't implement ViewManagerOnDemandReactPackage so we retrieve
// view managers via eager loading
for (ReactPackage reactPackage : packages) {
List<ViewManager> viewManagersInPackage =
reactPackage.createViewManagers(mBridgelessReactContext);
viewManagers.addAll(viewManagersInPackage);
}
}
Map<String, Object> constants =
UIManagerModule.createConstants(viewManagers, new HashMap<>(), new HashMap<>());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@
import com.facebook.react.bridge.ReactNoCrashSoftException;
import com.facebook.react.bridge.ReactSoftExceptionLogger;
import com.facebook.react.bridge.UIManager;
import com.facebook.react.config.ReactFeatureFlags;
import com.facebook.react.uimanager.common.UIManagerType;
import com.facebook.react.uimanager.events.EventDispatcher;
import com.facebook.react.uimanager.events.EventDispatcherProvider;

/** Helper class for {@link UIManager}. */
public class UIManagerHelper {
Expand Down Expand Up @@ -53,13 +53,19 @@ private static UIManager getUIManager(
@UIManagerType int uiManagerType,
boolean returnNullIfCatalystIsInactive) {
if (context.isBridgeless()) {
@Nullable UIManager uiManager = (UIManager) context.getJSIModule(JSIModuleType.UIManager);
UIManager uiManager = null;
if (uiManagerType == FABRIC) {
uiManager = (UIManager) context.getJSIModule(JSIModuleType.UIManager);
} else if (ReactFeatureFlags.unstable_useFabricInterop) {
// When Fabric Interop is enabled in Bridgeless mode, enable the legacy UIManager
uiManager = context.getNativeModule(UIManagerModule.class);
}

if (uiManager == null) {
ReactSoftExceptionLogger.logSoftException(
TAG,
new ReactNoCrashSoftException(
"Cannot get UIManager because the instance hasn't been initialized yet."));
return null;
}
return uiManager;
}
Expand Down Expand Up @@ -118,13 +124,6 @@ public static EventDispatcher getEventDispatcherForReactTag(ReactContext context
@Nullable
public static EventDispatcher getEventDispatcher(
ReactContext context, @UIManagerType int uiManagerType) {
// TODO T67518514 Clean this up once we migrate everything over to bridgeless mode
if (context.isBridgeless()) {
if (context instanceof ThemedReactContext) {
context = ((ThemedReactContext) context).getReactApplicationContext();
}
return ((EventDispatcherProvider) context).getEventDispatcher();
}
UIManager uiManager = getUIManager(context, uiManagerType, false);
if (uiManager == null) {
ReactSoftExceptionLogger.logSoftException(
Expand Down
Loading

0 comments on commit c8ad23a

Please sign in to comment.