React Native 源码浅析
1.RN是如何完成bundle文件加载的?
2.Native和JS之间是如何通讯的?
3.JS布局是怎么样被渲染到ReactRootView上的?
下面通过对RN源码(版本:0.40.0)的分析,尝试找找这3个问题的答案~
chengyuan-macpro:AwesomeProject chengyuan$ react-native -V
0.40.0
一、JSBundle加载过程
上一篇博客React Native 用法介绍有提到过将js文件打包成bundle,存放到assets目录下,RN页面启动后会加载bundle包,下面就具体看下bundle的加载过程~
public abstract class ReactActivity extends Activity
implements DefaultHardwareBackBtnHandler, PermissionAwareActivity {
private final ReactActivityDelegate mDelegate;
protected ReactActivity() {
mDelegate = createReactActivityDelegate();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mDelegate.onCreate(savedInstanceState);
}
//省略非关键代码
}
这里使用委托模式将 Activity 的生命周期及事件传递委托给 ReactActivityDelegate进行处理,这样用是为了让 ReactFragmentActivity 也能复用该处理逻辑。
在ReactActivityDelegate的onCreate中调用了loadApp
// ReactActivityDelegate.java
protected void loadApp(String appKey) {
// 创建RN根视图
mReactRootView = createRootView();
mReactRootView.startReactApplication(
getReactNativeHost().getReactInstanceManager(),
appKey,
getLaunchOptions());
// 将RN视图添加到Activity中
getPlainActivity().setContentView(mReactRootView);
}
通过startReactApplication完成ReactContext初始化
// ReactRootView.java
public void startReactApplication(
ReactInstanceManager reactInstanceManager,
String moduleName,
@Nullable Bundle launchOptions) {
// 确保再UI线程运行
UiThreadUtil.assertOnUiThread();
// 省略非关键代码
if (!mReactInstanceManager.hasStartedCreatingInitialContext()) {
mReactInstanceManager.createReactContextInBackground();
}
// 宽高计算完成后添加布局监听
if (mWasMeasured) {
attachToReactInstanceManager();
}
}
createReactContextInBackground这个方法只会在 Application 创建时调用一次,
重新加载 JS时将会调用 recreateReactContextInBackground方法。recreateReactContextInBackground调用了recreateReactContextInBackgroundInner方法
// XReactInstanceManagerImpl.java
private void recreateReactContextInBackgroundInner() {
UiThreadUtil.assertOnUiThread();
// 调试模式,从服务器加载 bundle
if (mUseDeveloperSupport && mJSMainModuleName != null) {
// 省略代码
return;
}
// 正式环境,从本地加载 bundle
recreateReactContextInBackgroundFromBundleLoader();
}
继续跟踪,发现启动异步任务ReactContextInitAsyncTask完成ReactContext初始化
// XReactInstanceManagerImpl.java
private final class ReactContextInitAsyncTask extends
AsyncTask<ReactContextInitParams, Void, Result<ReactApplicationContext>> {
@Override
protected void onPreExecute() {
if (mCurrentReactContext != null) {
tearDownReactContext(mCurrentReactContext);
mCurrentReactContext = null;
}
}
@Override
protected Result<ReactApplicationContext> doInBackground(ReactContextInitParams... params) {
// 省略代码
try {
JavaScriptExecutor jsExecutor = params[0].getJsExecutorFactory().create();
return Result.of(createReactContext(jsExecutor, params[0].getJsBundleLoader()));
} catch (Exception e) {
return Result.of(e);
}
}
// 省略代码
}
doInBackground中调用createReactContext创建ReactContext,通过第二个参数params[0].getJsBundleLoader()携带的bundle信息决定从哪里加载bundle文件
// XReactInstanceManagerImpl.java
private ReactApplicationContext createReactContext(
JavaScriptExecutor jsExecutor,
JSBundleLoader jsBundleLoader) {
// 省略代码
catalystInstance.runJSBundle();
return reactContext;
}
runJSBundle执行了mJSBundleLoader.loadScript(CatalystInstanceImpl.this);由于mJSBundleLoader由createAssetLoader创建,所以会调用如下方法
public