Support custom DevSupportManager (#31841)

Summary:
to open possibilities for some DX enhancement, the pr introduces `DevSupportManagerFactory` customization. applications could implement a different DevSupportManager in ReactNativeHost.

## Changelog

[Internal] [Added] - Support custom DevSupportManager

Pull Request resolved: https://github.com/facebook/react-native/pull/31841

Test Plan: this pr just introduces some new interfaces and should not break existing functionalities.

Reviewed By: RSNara

Differential Revision: D30878134

Pulled By: yungsters

fbshipit-source-id: ccdf798caa322b07a876da8312b97002da057388
This commit is contained in:
Kudo Chien
2021-09-22 10:33:14 -07:00
committed by Facebook GitHub Bot
parent 88021894f2
commit 369b28ce01
5 changed files with 127 additions and 74 deletions
@@ -0,0 +1,97 @@
/*
* Copyright (c) Facebook, Inc. and its 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.react.devsupport.interfaces.DevBundleDownloadListener;
import com.facebook.react.devsupport.interfaces.DevSupportManager;
import com.facebook.react.packagerconnection.RequestHandler;
import java.lang.reflect.Constructor;
import java.util.Map;
/**
* A simple factory that creates instances of {@link 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, {@link
* DisabledDevSupportManager} is returned instead.
*/
public class DefaultDevSupportManagerFactory implements DevSupportManagerFactory {
private static final String DEVSUPPORT_IMPL_PACKAGE = "com.facebook.react.devsupport";
private static final String DEVSUPPORT_IMPL_CLASS = "BridgeDevSupportManager";
public DevSupportManager create(
Context applicationContext,
ReactInstanceDevHelper reactInstanceDevHelper,
@Nullable String packagerPathForJSBundleName,
boolean enableOnCreate,
int minNumShakes) {
return create(
applicationContext,
reactInstanceDevHelper,
packagerPathForJSBundleName,
enableOnCreate,
null,
null,
minNumShakes,
null);
}
@Override
public DevSupportManager create(
Context applicationContext,
ReactInstanceDevHelper reactInstanceManagerHelper,
@Nullable String packagerPathForJSBundleName,
boolean enableOnCreate,
@Nullable RedBoxHandler redBoxHandler,
@Nullable DevBundleDownloadListener devBundleDownloadListener,
int minNumShakes,
@Nullable Map<String, RequestHandler> customPackagerCommandHandlers) {
if (!enableOnCreate) {
return new DisabledDevSupportManager();
}
try {
// 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.
String className =
new StringBuilder(DEVSUPPORT_IMPL_PACKAGE)
.append(".")
.append(DEVSUPPORT_IMPL_CLASS)
.toString();
Class<?> devSupportManagerClass = Class.forName(className);
Constructor constructor =
devSupportManagerClass.getConstructor(
Context.class,
ReactInstanceDevHelper.class,
String.class,
boolean.class,
RedBoxHandler.class,
DevBundleDownloadListener.class,
int.class,
Map.class);
return (DevSupportManager)
constructor.newInstance(
applicationContext,
reactInstanceManagerHelper,
packagerPathForJSBundleName,
true,
redBoxHandler,
devBundleDownloadListener,
minNumShakes,
customPackagerCommandHandlers);
} catch (Exception e) {
throw new RuntimeException(
"Requested enabled DevSupportManager, but BridgeDevSupportManager class was not found"
+ " or could not be created",
e);
}
}
}
@@ -12,39 +12,10 @@ import androidx.annotation.Nullable;
import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener;
import com.facebook.react.devsupport.interfaces.DevSupportManager;
import com.facebook.react.packagerconnection.RequestHandler;
import java.lang.reflect.Constructor;
import java.util.Map;
/**
* A simple factory that creates instances of {@link 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, {@link
* DisabledDevSupportManager} is returned instead.
*/
public class DevSupportManagerFactory {
private static final String DEVSUPPORT_IMPL_PACKAGE = "com.facebook.react.devsupport";
private static final String DEVSUPPORT_IMPL_CLASS = "BridgeDevSupportManager";
public static DevSupportManager create(
Context applicationContext,
ReactInstanceDevHelper reactInstanceDevHelper,
@Nullable String packagerPathForJSBundleName,
boolean enableOnCreate,
int minNumShakes) {
return create(
applicationContext,
reactInstanceDevHelper,
packagerPathForJSBundleName,
enableOnCreate,
null,
null,
minNumShakes,
null);
}
public static DevSupportManager create(
public interface DevSupportManagerFactory {
DevSupportManager create(
Context applicationContext,
ReactInstanceDevHelper reactInstanceManagerHelper,
@Nullable String packagerPathForJSBundleName,
@@ -52,45 +23,5 @@ public class DevSupportManagerFactory {
@Nullable RedBoxHandler redBoxHandler,
@Nullable DevBundleDownloadListener devBundleDownloadListener,
int minNumShakes,
@Nullable Map<String, RequestHandler> customPackagerCommandHandlers) {
if (!enableOnCreate) {
return new DisabledDevSupportManager();
}
try {
// 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.
String className =
new StringBuilder(DEVSUPPORT_IMPL_PACKAGE)
.append(".")
.append(DEVSUPPORT_IMPL_CLASS)
.toString();
Class<?> devSupportManagerClass = Class.forName(className);
Constructor constructor =
devSupportManagerClass.getConstructor(
Context.class,
ReactInstanceDevHelper.class,
String.class,
boolean.class,
RedBoxHandler.class,
DevBundleDownloadListener.class,
int.class,
Map.class);
return (DevSupportManager)
constructor.newInstance(
applicationContext,
reactInstanceManagerHelper,
packagerPathForJSBundleName,
true,
redBoxHandler,
devBundleDownloadListener,
minNumShakes,
customPackagerCommandHandlers);
} catch (Exception e) {
throw new RuntimeException(
"Requested enabled DevSupportManager, but BridgeDevSupportManager class was not found"
+ " or could not be created",
e);
}
}
@Nullable Map<String, RequestHandler> customPackagerCommandHandlers);
}