mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Add NativeLogBox module on Android
Summary: This diff adds a NativeLogBox module implementation on Android to manage rendering LogBox the way we render RedBox, except rendering a React Native component instead of a native view. The strategy here is: - initialize: will create a React rootview and render it. - show: will add the rootview to a dialog and display the dialog. - hide: will remove the rootview from it's parent, dismiss the dialog, and release the reference to the activity to prevent leaks. Most of this is copied from the way RedBox works, the difference here is that we eagerly initialize the rootview with the `initialize` function so that it's warm by the time the dialog needs to render. Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D18768517 fbshipit-source-id: 2510d6c186ccf73153ef9372c736c9e0c71bbc7d
This commit is contained in:
committed by
Facebook Github Bot
parent
6b22a4e802
commit
e272089524
@@ -16,6 +16,7 @@ import androidx.annotation.Nullable;
|
||||
import com.facebook.react.bridge.NativeModule;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactMarker;
|
||||
import com.facebook.react.devsupport.LogBoxModule;
|
||||
import com.facebook.react.module.annotations.ReactModule;
|
||||
import com.facebook.react.module.annotations.ReactModuleList;
|
||||
import com.facebook.react.module.model.ReactModuleInfo;
|
||||
@@ -49,6 +50,7 @@ import java.util.Map;
|
||||
DeviceInfoModule.class,
|
||||
DevSettingsModule.class,
|
||||
ExceptionsManagerModule.class,
|
||||
LogBoxModule.class,
|
||||
HeadlessJsTaskSupportModule.class,
|
||||
SourceCodeModule.class,
|
||||
TimingModule.class,
|
||||
@@ -94,6 +96,7 @@ import java.util.Map;
|
||||
DeviceInfoModule.class,
|
||||
DevSettingsModule.class,
|
||||
ExceptionsManagerModule.class,
|
||||
LogBoxModule.class,
|
||||
HeadlessJsTaskSupportModule.class,
|
||||
SourceCodeModule.class,
|
||||
TimingModule.class,
|
||||
@@ -142,6 +145,8 @@ import java.util.Map;
|
||||
return new DevSettingsModule(reactContext, mReactInstanceManager.getDevSupportManager());
|
||||
case ExceptionsManagerModule.NAME:
|
||||
return new ExceptionsManagerModule(mReactInstanceManager.getDevSupportManager());
|
||||
case LogBoxModule.NAME:
|
||||
return new LogBoxModule(reactContext, mReactInstanceManager.getDevSupportManager());
|
||||
case HeadlessJsTaskSupportModule.NAME:
|
||||
return new HeadlessJsTaskSupportModule(reactContext);
|
||||
case SourceCodeModule.NAME:
|
||||
|
||||
@@ -304,6 +304,27 @@ public class ReactInstanceManager {
|
||||
public JavaScriptExecutorFactory getJavaScriptExecutorFactory() {
|
||||
return ReactInstanceManager.this.getJSExecutorFactory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable View createRootView(String appKey) {
|
||||
Activity currentActivity = getCurrentActivity();
|
||||
if (currentActivity != null) {
|
||||
ReactRootView rootView = new ReactRootView(currentActivity);
|
||||
|
||||
rootView.startReactApplication(ReactInstanceManager.this, appKey, null);
|
||||
|
||||
return rootView;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyRootView(View rootView) {
|
||||
if (rootView instanceof ReactRootView) {
|
||||
((ReactRootView) rootView).unmountReactApplication();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.hardware.SensorManager;
|
||||
import android.util.Pair;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -377,6 +378,14 @@ public class DevSupportManagerImpl
|
||||
}
|
||||
}
|
||||
|
||||
public @Nullable View createRootView(String appKey) {
|
||||
return mReactInstanceManagerHelper.createRootView(appKey);
|
||||
}
|
||||
|
||||
public void destroyRootView(View rootView) {
|
||||
mReactInstanceManagerHelper.destroyRootView(rootView);
|
||||
}
|
||||
|
||||
private void hideDevOptionsDialog() {
|
||||
if (mDevOptionsDialog != null) {
|
||||
mDevOptionsDialog.dismiss();
|
||||
|
||||
+9
@@ -7,6 +7,7 @@
|
||||
|
||||
package com.facebook.react.devsupport;
|
||||
|
||||
import android.view.View;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.react.bridge.DefaultNativeModuleCallExceptionHandler;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
@@ -40,6 +41,14 @@ public class DisabledDevSupportManager implements DevSupportManager {
|
||||
@Override
|
||||
public void showNewJSError(String message, ReadableArray details, int errorCookie) {}
|
||||
|
||||
@Override
|
||||
public @Nullable View createRootView(String appKey) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyRootView(View rootView) {}
|
||||
|
||||
@Override
|
||||
public void updateJSError(String message, ReadableArray details, int errorCookie) {}
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import com.facebook.react.R;
|
||||
|
||||
/** Dialog for displaying JS errors in LogBox. */
|
||||
public class LogBoxDialog extends Dialog {
|
||||
public LogBoxDialog(Activity context, View reactRootView) {
|
||||
super(context, R.style.Theme_Catalyst_LogBox);
|
||||
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
setContentView(reactRootView);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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.app.Activity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.common.logging.FLog;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.UiThreadUtil;
|
||||
import com.facebook.react.common.ReactConstants;
|
||||
import com.facebook.react.devsupport.interfaces.DevSupportManager;
|
||||
import com.facebook.react.module.annotations.ReactModule;
|
||||
|
||||
@ReactModule(name = LogBoxModule.NAME)
|
||||
public class LogBoxModule extends ReactContextBaseJavaModule {
|
||||
|
||||
public static final String NAME = "LogBox";
|
||||
|
||||
private final DevSupportManager mDevSupportManager;
|
||||
private @Nullable View mReactRootView;
|
||||
private @Nullable LogBoxDialog mLogBoxDialog;
|
||||
|
||||
public LogBoxModule(ReactApplicationContext reactContext, DevSupportManager devSupportManager) {
|
||||
super(reactContext);
|
||||
|
||||
mDevSupportManager = devSupportManager;
|
||||
UiThreadUtil.runOnUiThread(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mReactRootView == null) {
|
||||
mReactRootView = mDevSupportManager.createRootView("LogBox");
|
||||
if (mReactRootView == null) {
|
||||
FLog.e(
|
||||
ReactConstants.TAG,
|
||||
"Unable to launch logbox because react was unable to create the root view");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void show() {
|
||||
UiThreadUtil.runOnUiThread(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mLogBoxDialog == null) {
|
||||
Activity context = getCurrentActivity();
|
||||
if (context == null || context.isFinishing()) {
|
||||
FLog.e(
|
||||
ReactConstants.TAG,
|
||||
"Unable to launch logbox because react activity "
|
||||
+ "is not available, here is the error that logbox would've displayed: ");
|
||||
return;
|
||||
}
|
||||
mLogBoxDialog = new LogBoxDialog(context, mReactRootView);
|
||||
mLogBoxDialog.setCancelable(false);
|
||||
mLogBoxDialog.show();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void hide() {
|
||||
UiThreadUtil.runOnUiThread(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mLogBoxDialog != null) {
|
||||
if (mReactRootView.getParent() != null) {
|
||||
((ViewGroup) mReactRootView.getParent()).removeView(mReactRootView);
|
||||
}
|
||||
mLogBoxDialog.dismiss();
|
||||
mLogBoxDialog = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCatalystInstanceDestroy() {
|
||||
UiThreadUtil.runOnUiThread(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mReactRootView != null) {
|
||||
mDevSupportManager.destroyRootView(mReactRootView);
|
||||
mReactRootView = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
+6
@@ -8,6 +8,7 @@
|
||||
package com.facebook.react.devsupport;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.view.View;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.react.bridge.JavaJSExecutor;
|
||||
import com.facebook.react.bridge.JavaScriptExecutorFactory;
|
||||
@@ -32,4 +33,9 @@ public interface ReactInstanceManagerDevHelper {
|
||||
Activity getCurrentActivity();
|
||||
|
||||
JavaScriptExecutorFactory getJavaScriptExecutorFactory();
|
||||
|
||||
@Nullable
|
||||
View createRootView(String appKey);
|
||||
|
||||
void destroyRootView(View rootView);
|
||||
}
|
||||
|
||||
+6
@@ -7,6 +7,7 @@
|
||||
|
||||
package com.facebook.react.devsupport.interfaces;
|
||||
|
||||
import android.view.View;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.react.bridge.NativeModuleCallExceptionHandler;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
@@ -25,6 +26,11 @@ public interface DevSupportManager extends NativeModuleCallExceptionHandler {
|
||||
|
||||
void addCustomDevOption(String optionName, DevOptionHandler optionHandler);
|
||||
|
||||
@Nullable
|
||||
View createRootView(String appKey);
|
||||
|
||||
void destroyRootView(View rootView);
|
||||
|
||||
void showNewJSError(String message, ReadableArray details, int errorCookie);
|
||||
|
||||
void updateJSError(final String message, final ReadableArray details, final int errorCookie);
|
||||
|
||||
Reference in New Issue
Block a user