Note
:本文基于RNAndroid0.32對Debug模式進行分析。
在RN開發工作中,為了便于開發者調試自己的業務代碼,Facebook提供了Debug模式,可以快速地幫助我們捕捉相關信息,為開發工作提供了相當大的便利。通過在ReactNativeHost
開啟Debug開關,即可在開發工作中使用Debug模式。整個Debug框架結構如下:
從類圖中可以看到,RN Debug框架的的核心類是DevSupportManager
,它作為Debug模式的接口可以為RN管理類ReactInstanceManager
提供開發調試狀態下的各項信息和狀態。
DevSupportManager
提供了如下方法:
//展示Java層和JS層的異常
void showNewJavaError(String message, Throwable e);
void showNewJSError(String message, ReadableArray details, int errorCookie);
void updateJSError(final String message, final ReadableArray details, final int errorCookie);
//DevOption menu
void addCustomDevOption(String optionName, DevOptionHandler optionHandler);
void setDevSupportEnabled(boolean isDevSupportEnabled);
void hideRedboxDialog();
void showDevOptionsDialog();
boolean getDevSupportEnabled();
DeveloperSettings getDevSettings();
//RIM新創建了ReactContext
void onNewReactContextCreated(ReactContext reactContext);
//銷毀ReactContext
void onReactInstanceDestroyed(ReactContext reactContext);
//通過DevServerHelper與Server交互,獲取jsbundle文件路徑
String getSourceMapUrl();
String getSourceUrl();
String getJSBundleURLForRemoteDebugging();
String getHeapCaptureUploadUrl();
//獲取本地緩存的JS文件
String getDownloadedJSBundleFile();
//判斷RIM是否應使用緩存中的JS文件來代替asset目錄中的文件,
//Debug模式下,本地緩存中有JS文件則會調用此方法
boolean hasUpToDateJSBundleInCache();
//重載開發者的設置選項
void reloadSettings();
//重新加載JS
void handleReloadJS();
//查詢packager server狀態
void isPackagerRunning(DevServerHelper.PackagerStatusCallback callback);
//獲取異常信息
String getLastErrorTitle();
StackFrame[] getLastErrorStack();
該接口有兩個實現類:DevSupportManagerImpl
和DisabledDevSupportManager
:
在Debug模式下,RN使用了DevSupportManagerImpl
,而在release下,則會使用DisabledDevSupportManager
。
其中,DevSupportManagerImpl
為開發者調試程序提供如下功能:
- 通過RedBox顯示JS code錯誤
- 顯示Debug menu(用于重新加載JS文件,調試JS代碼)
- 與Server交互,更新JSBundle
- 控制重載JSBundle的廣播接收器
- 控制識別motion sensor的監聽(搖動手機喚起Debug Menu)
- 啟動developers settings 視圖
I. DevSupportManager
的構造和初始化
1.1 構造
在RN運行時環境的啟動過程中,RIM利用工廠模式在其實現類XReactInstanceManagerImpl
的構造函數中,創建了DevSupportManager
:
public static DevSupportManager create(
Context applicationContext,
ReactInstanceDevCommandsHandler reactInstanceCommandsHandler,
@Nullable String packagerPathForJSBundleName,
boolean enableOnCreate,
@Nullable RedBoxHandler redBoxHandler) {
if (!enableOnCreate) {//生產環境
return new DisabledDevSupportManager();
}
try {
String className =
new StringBuilder(DEVSUPPORT_IMPL_PACKAGE)
.append(".")
.append(DEVSUPPORT_IMPL_CLASS)
.toString();
Class<?> devSupportManagerClass =
Class.forName(className);
Constructor constructor =
devSupportManagerClass.getConstructor(
Context.class,
ReactInstanceDevCommandsHandler.class,
String.class,
boolean.class,
RedBoxHandler.class);
return (DevSupportManager) constructor.newInstance(
applicationContext,
reactInstanceCommandsHandler,
packagerPathForJSBundleName,
true,
redBoxHandler);
} catch (Exception e) {
throw new RuntimeException(
"Requested enabled DevSupportManager, but DevSupportManagerImpl class was not found" +
" or could not be created",
e);
}
}
當未開啟Debug模式時,返回了DisabledDevSupportManager
對象,否則通過反射創建DevSupportManagerImpl
對象。
1.2 初始化
在DevSupportManagerImpl
的構造方法中,DevSupportManager
對自身以及所依賴的類對象進行了初始化工作:
public DevSupportManagerImpl(
Context applicationContext,//Android Context
ReactInstanceDevCommandsHandler reactInstanceCommandsHandler,//該接口用于基于開發者設定的選項處理重建RN實例請求
@Nullable String packagerPathForJSBundleName,
boolean enableOnCreate,
@Nullable RedBoxHandler redBoxHandler) {//RedBoxHandler創建時為null
//用于處理DevSupportManager重新創建React Instance的請求
mReactInstanceCommandsHandler = reactInstanceCommandsHandler;
//Android Application
mApplicationContext = applicationContext;
//RN主module名稱,用于從Package server獲取JSbundle
mJSAppBundleName = packagerPathForJSBundleName;
//Dev setting的helper類,用于獲取開發者的settings
//這些settings都作為SharedPreference存于本地
mDevSettings = new DevInternalSettings(applicationContext, this);
//Debug server的helper類
mDevServerHelper = new DevServerHelper(
mDevSettings,
new DevServerHelper.PackagerCommandListener() {
@Override
public void onReload() {
UiThreadUtil.runOnUiThread(new Runnable() {
@Override
public void run() {
handleReloadJS();
}
});
}
});
// Prepare shake gesture detector (will be started/stopped from #reload)
mShakeDetector = new ShakeDetector(new ShakeDetector.ShakeListener() {
@Override
public void onShake() {
showDevOptionsDialog();
}
});
// Prepare reload APP broadcast receiver (will be registered/unregistered from #reload)
mReloadAppBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (DevServerHelper.getReloadAppAction(context).equals(action)) {
if (intent.getBooleanExtra(DevServerHelper.RELOAD_APP_EXTRA_JS_PROXY, false)) {
mDevSettings.setRemoteJSDebugEnabled(true);
mDevServerHelper.launchJSDevtools();
} else {
mDevSettings.setRemoteJSDebugEnabled(false);
}
handleReloadJS();
}
}
};
//從package server拉取的JSBundle文件被臨時存放為"ReactNativeDevBundle.js"
mJSBundleTempFile = new File(applicationContext.getFilesDir(), JS_BUNDLE_FILE_NAME);
mDefaultNativeModuleCallExceptionHandler = new DefaultNativeModuleCallExceptionHandler();
setDevSupportEnabled(enableOnCreate);
mRedBoxHandler = redBoxHandler;//默認為Null
}
在構造方法中,DevSupportManagerImpl
通過setDevSupportEnabled
方法進一步調用了reload
方法,通過各項debug配置進行進一步的初始化操作:
private void reload() {
// reload settings, show/hide debug overlay if required & start/stop shake detector
if (mIsDevSupportEnabled) {
// update visibility of FPS debug overlay depending on the settings
if (mDebugOverlayController != null) {
mDebugOverlayController.setFpsDebugViewVisible(mDevSettings.isFpsDebugEnabled());
}
// start shake gesture detector
if (!mIsShakeDetectorStarted) {
mShakeDetector.start(
(SensorManager) mApplicationContext.getSystemService(Context.SENSOR_SERVICE));
mIsShakeDetectorStarted = true;
}
// register reload app broadcast receiver
if (!mIsReceiverRegistered) {
IntentFilter filter = new IntentFilter();
filter.addAction(DevServerHelper.getReloadAppAction(mApplicationContext));
mApplicationContext.registerReceiver(mReloadAppBroadcastReceiver, filter);
mIsReceiverRegistered = true;
}
if (mDevSettings.isReloadOnJSChangeEnabled()) {
mDevServerHelper.startPollingOnChangeEndpoint(
new DevServerHelper.OnServerContentChangeListener() {
@Override
public void onServerContentChanged() {
handleReloadJS();
}
});
} else {
mDevServerHelper.stopPollingOnChangeEndpoint();
}
} else {
// hide FPS debug overlay
if (mDebugOverlayController != null) {
mDebugOverlayController.setFpsDebugViewVisible(false);
}
// stop shake gesture detector
if (mIsShakeDetectorStarted) {
mShakeDetector.stop();
mIsShakeDetectorStarted = false;
}
// unregister app reload broadcast receiver
if (mIsReceiverRegistered) {
mApplicationContext.unregisterReceiver(mReloadAppBroadcastReceiver);
mIsReceiverRegistered = false;
}
// hide redbox dialog
if (mRedBoxDialog != null) {
mRedBoxDialog.dismiss();
}
// hide dev options dialog
if (mDevOptionsDialog != null) {
mDevOptionsDialog.dismiss();
}
mDevServerHelper.stopPollingOnChangeEndpoint();
}
}