您的位置:首页 > Web前端 > React

react-native源码分析系列一

2015-12-08 14:34 751 查看
react-native源码目前我看到过的最好的分析文章是qq空间分析的这篇。
http://zhuanlan.zhihu.com/magilu/20259704
不得不说大厂推出的分析确实牛逼,逻辑清晰。

不过由于博主自己也读过源码,也做过一些分析,因此准备将这些分析整理一下放出来(这个系列会比较长。

react-native 官网https://github.com/facebook/react-native

这里不介绍怎么使用,有兴趣的可以参考我的个人项目https://github.com/xiaoshenke/React-Online-News

照例我们从demo开始分析,我看的demo是react-native/Examples/Movies工程。

以下rn代指react-native。





可以看到初始化代码只有两行。
先看第一行代码,初始化构建ReactInstanceManager。
ReactInstanceManager
mReactInstanceManager = ReactInstanceManager.builder()
//builder模式
.setApplication(getApplication())
.setBundleAssetName("MoviesApp.android.bundle") //bundleName貌似可以随便取
.setJSMainModuleName("Examples/Movies/MoviesApp.android") //这里的名字必须是对应js文件入口的名字
.addPackage(new MainReactPackage()) //注意名字 这里就是主要的rn包//官网有说明怎么加自定义包
.setUseDeveloperSupport(true) //manifest中的DevSettingsActivity
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();

public ReactInstanceManager build() {
return new ReactInstanceManager(
Assertions.assertNotNull(
mApplicatio
4000
n,
"Application property has not been set with this builder"),
mBundleAssetName,
mJSMainModuleName,
mPackages,
mUseDeveloperSupport,
mBridgeIdleDebugListener,
Assertions.assertNotNull(mInitialLifecycleState, "Initial lifecycle state was not set"));
}

private ReactInstanceManager( //ReactInstanceManager构造函数
Context applicationContext,
@Nullable String bundleAssetName,
@Nullable String jsMainModuleName,
List<ReactPackage> packages,
boolean useDeveloperSupport,
@Nullable NotThreadSafeBridgeIdleDebugListener bridgeIdleDebugListener,
LifecycleState initialLifecycleState) {

initializeSoLoaderIfNecessary(applicationContext);
mApplicationContext = applicationContext;

mBundleAssetName
= bundleAssetName; //这里传入的是“MoviesApp.android.bundle"
mJSMainModuleName = jsMainModuleName; //这里传入的是“Examples/Movies/MoviesApp.android”
mPackages = packages;
mUseDeveloperSupport = useDeveloperSupport; //这里传入的是true
// We need to instantiate DevSupportManager regardless to the useDeveloperSupport option,
// although will prevent dev support manager from displaying any options or dialogs by
// checking useDeveloperSupport option before calling setDevSupportEnabled on this manager
//
TODO(6803830): Don't instantiate devsupport manager when useDeveloperSupport is false
mDevSupportManager = new DevSupportManager( //调试用 这里不分析
applicationContext,
mDevInterface,
mJSMainModuleName,
useDeveloperSupport);
mBridgeIdleDebugListener = bridgeIdleDebugListener;
mLifecycleState = initialLifecycleState;
}

////////通过以上过程构造出了ReactInstanceManager
然后是第二行代码。ReactRootView.startReactApplication
我们看一下ReactRootView是干啥的。



////////所以ReactRootView是React工程中的rootview,并且负责分发各种touch事件。
public class ReactRootView extends SizeMonitoringFrameLayout implements
RootView
public class SizeMonitoringFrameLayout extends
FrameLayout
public interface RootView {
void onChildStartedNativeGesture(MotionEvent androidEvent);
}

继续看真正的初始化入口startReactApplication函数。
/**
* Schedule rendering of the react component rendered by the JS application from the given JS
* module (@{param moduleName}) using provided {@param reactInstanceManager} to attach to the
* JS context of that manager. Extra parameter {@param launchOptions} can be used to pass initial
* properties for the react component.
*/
public void startReactApplication(ReactInstanceManager reactInstanceManager,
String moduleName,@Nullable Bundle launchOptions) { //这里的moduleName传入的是“MoviesApp”
//如果你懂一点React.js的话 js代码大概长这样 module.exports=MoviesApp-->就是说这是一个可以被其他mudule调用的对象
mReactInstanceManager = reactInstanceManager;
mJSModuleName = moduleName;
mLaunchOptions = launchOptions;
if (mWasMeasured && mIsAttachedToWindow) {

mReactInstanceManager.attachMeasuredRootView(this); //真正的入口
mIsAttachedToInstance = true;
getViewTreeObserver().addOnGlobalLayoutListener(mKeyboardListener);
} else {
mAttachScheduled = true;
}
}

/**
* Attach given {@param rootView} to a catalyst instance manager and start JS application
using
* JS module provided by {@link ReactRootView#getJSModuleName}. This view will then be tracked
* by this manager and in case of catalyst instance restart it will be re-attached.
*/
/* package */ void attachMeasuredRootView(ReactRootView rootView) {
UiThreadUtil.assertOnUiThread();
mAttachedRootViews.add(rootView);
if (mCurrentReactContext == null) {
initializeReactContext();
//生成重要对象ReactContext(很眼熟是不是~ 这个context继承自android的ContextWrapper
//关于context 老罗的这篇文章http://blog.csdn.net/luoshengyang/article/details/8201936里
//可以看到Activity是继承自ContextWrapper的
} else {
attachMeasuredRootViewToInstance(rootView, mCurrentReactContext.getCatalystInstance());
}
}

initializeReactContext —> createReactContext //中间一系列调用略去

/**
*
@return instance of {@link ReactContext} configured a {@link CatalystInstance} set
*/
private ReactApplicationContext createReactContext(
JavaScriptExecutor jsExecutor,JSBundleLoader jsBundleLoader) {
...
} ////重要函数 //后面一篇文章具体分析这个函数
//剧透一下 大概就是干了生成一个context 注册了一堆双方共用的控件 服务 其实就是打通了js native代码交互的通道

createReactContext后会调用attachMeasuredRootViewToInstance
private void attachMeasuredRootViewToInstance(
ReactRootView rootView,CatalystInstance catalystInstance) {
UiThreadUtil.assertOnUiThread();
// Reset view content as it's going to be populated by the application content from JS
rootView.removeAllViews();
rootView.setId(View.NO_ID);
UIManagerModule uiManagerModule = catalystInstance.getNativeModule(UIManagerModule.class); //注意名字 nativeModule
//统一处理js java两边对应控件的manager
// rn是用js封装了一套native的对应控件 比如native里的DrawerLayout js那边对应的是drawerLayoutAndroid
//因此需要一个manager来进行协调
int
rootTag = uiManagerModule.addMeasuredRootView(rootView);
//这里调用了native的addMeasuredRootView
@Nullable Bundle launchOptions = rootView.getLaunchOptions();
WritableMap initialProps = launchOptions != null
? Arguments.fromBundle(launchOptions)
: Arguments.createMap();
String jsAppModuleName = rootView.getJSModuleName(); //注意名字 jsModule
WritableNativeMap appParams = new WritableNativeMap();
appParams.putDouble("rootTag", rootTag);
appParams.putMap("initialProps", initialProps);
catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams);
//这里获取了js的一个模块 AppRegistry 并调用了js模块里的runApplication函数
}

ctrl f搜索runApplication。可以看到对应的js文件及runApplication这个接口。



AppRegistry.js文件。



好了,第一篇文章先到这里。
总结一下,这篇文章分析了react-native初始化的过程(两行代码。
第一行代码,构造出一个reactInstanceManager类(这个类就是一个调度类。
第二行代码,ReactRootView.startApplication(这里是初始化入口,包括生成重要的ReactContext 以及后面的打通js java native通信等等。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: