Android-ReactNative渲染流程

注册组件

在RN项目中必须使用AppRegistry.registerComponent(appkey,component)来注册组件。在上一篇中的runJSBundle里面,加载js文件后,交给JSC运行(evaluateSourceCode函数),就会开始执行index.js,执行里面的代码,在index.js里面就会调用AppRegistry来注册组件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
registerComponent(
appKey: string,
componentProvider: ComponentProvider,
section?: boolean,
): string {
// 为该appkey准备了一个runnable
runnables[appKey] = {
componentProvider,
run: appParameters => {
// appParameters启动参数,来自原生调用传入的
// 开始渲染
renderApplication(
componentProviderInstrumentationHook(componentProvider),
appParameters.initialProps,
appParameters.rootTag,
wrapperComponentProvider && wrapperComponentProvider(appParameters),
appParameters.fabric,
);
},
};
if (section) {
sections[appKey] = runnables[appKey];
}
return appKey;
},

启动组件

在上一篇说到,当Context创建完毕后,会启动js组件(defaultJSEntryPoint函数),通过调用js函数AppRegister.runApplication来启动。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
runApplication(appKey: string, appParameters: any): void {
invariant(
runnables[appKey] && runnables[appKey].run,
'Application ' +
appKey +
' has not been registered.\n\n' +
"Hint: This error often happens when you're running the packager " +
'(local dev server) from a wrong folder. For example you have ' +
'multiple apps and the packager is still running for the app you ' +
'were working on before.\nIf this is the case, simply kill the old ' +
'packager instance (e.g. close the packager terminal window) ' +
'and start the packager in the correct app folder (e.g. cd into app ' +
"folder and run 'npm start').\n\n" +
'This error can also happen due to a require() error during ' +
'initialization or failure to call AppRegistry.registerComponent.\n\n',
);

// 运行该appkey注册的runnable
runnables[appKey].run(appParameters);
}

解析组件

通过ReactNativeRenderer来解析组件视图,然后会调用组件的render方法获取渲染内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function renderApplication<Props: Object>(
RootComponent: React.ComponentType<Props>,
initialProps: Props,
rootTag: any,
WrapperComponent?: ?React.ComponentType<*>,
fabric?: boolean,
) {
let renderable = (
<AppContainer rootTag={rootTag} WrapperComponent={WrapperComponent}>
<RootComponent {...initialProps} rootTag={rootTag} />
</AppContainer>
);
if (
RootComponent.prototype != null &&
RootComponent.prototype.unstable_isAsyncReactComponent === true
) {
const AsyncMode = React.unstable_AsyncMode;
renderable = <AsyncMode>{renderable}</AsyncMode>;
}
if (fabric) {
require('ReactFabric').render(renderable, rootTag);
} else {
//
require('ReactNative').render(renderable, rootTag);
}
}

Android-ReactNative通信流程

ReactAndroid

ReactActivity

一个ReactActivity对应着一个RN组件(通过AppRegistry.registerComponent注册),而ReactActivity的功能都包装在ReactActivityDelegate中。

ReactActivity实际上类似于一个浏览器窗口,在一个窗口内对一个网页访问,并进行页面的跳转和回退。

ReactRootView

ReactRootView是ReactNative组件的根布局,是一个ViewGroup(继承自FrameLayout),所有的RN视图都会被添加到该布局下,然后该布局会被添加到Activity上,因此每个ReactActivity都有着一个ReactRootView。RN组件内的页面跳转实际上就是在该ReactRootView上重绘。

ReactNativeHost

每个RN组件的配置,设置jsbundle文件的文件名和js模块名称以及开发模式等。

  • getJSMainModuleName : 远程服务获取bundle时,获取的模块名称
  • getBundleAssetName : 本地加载bundle时,获取asset文件的文件名
  • getUseDeveloperSupport : 是否开启debug模式
  • getPackages : 配置js和native通信module,必须包含MainReactPackage
  • ReactInstanceManager : 与js通信的管理类,CatalystInstance是一个异步JSC桥梁,提供了调用js方法的JavaAPI

每个ReactActivity都需要一个Host配置,对应不同的jsbundle配置。在ReactActivity中默认会去获取Application中的ReactNativeHost,默认Application需要继承ReactApplication接口,并实现其getReactNativeHost方法。如果我们不想在Application中实现该接口(必须我们有多个RN组件需要多个ReactNativeHost对象),就必须重写ReactActivity中getReactNativeHost方法。

1
2
3
protected ReactNativeHost getReactNativeHost() {
return ((ReactApplication) getPlainActivity().getApplication()).getReactNativeHost();
}

加载RN组件

启动ReactActivity

首先启动ReactActivity,创建ReactActivity时会同时创建ReactActivityDelegate,ReactActivity的getMainComponentName方法对应的是js组件的注册名称(js中AppRegistry.registerComponent注册时的appKey),在ReactActivity的onCreate回调中,开始加载该js组件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
protected void onCreate(Bundle savedInstanceState) {
if (mMainComponentName != null) {
loadApp(mMainComponentName);
}
}

protected void loadApp(String appKey) {
if (mReactRootView != null) {
throw new IllegalStateException("Cannot loadApp while app is already running.");
}
// 创建根布局
mReactRootView = createRootView();
// 启动该js组件,加载jsbundle文件,并通过jsc来构建virtualDom来解析js组件
// 将js控件解析成对应的View,并添加到根布局上,组成视图树。
mReactRootView.startReactApplication(
// 创建js通信管理器
getReactNativeHost().getReactInstanceManager(),
appKey,
getLaunchOptions());
// 将解析完成的布局放到Activity中渲染
getPlainActivity().setContentView(mReactRootView);
}
创建ReactRootView

ReactRootView继承FrameLayout,是整个RN组件的根布局,解析出来的View都会添加到该布局上。该View主要处理了测量和事件的分发拦截,包括触摸、手势、焦点等。还设置了OnGlobalLayout监听,该方法会在布局重新layout或者系统UI发生改变时触发(具体在ViewRootImpl.performTraversals中),在该监听中将发生的改变通知给js模块。

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
public void onGlobalLayout() {
if (mReactInstanceManager == null || !mIsAttachedToInstance ||
mReactInstanceManager.getCurrentReactContext() == null) {
return;
}
// 键盘状态改变
checkForKeyboardEvents();
// 屏幕方向改变
checkForDeviceOrientationChanges();
// 屏幕分辨率改变
checkForDeviceDimensionsChanges();
}
创建ReactInstanceManager

ReactInstanceManager是一个CatalystInstance实例的管理者,而CatalysInstance是一个异步的JSC桥梁,负责与js进行通信,调用js方法。

Android-ReactNative通信流程

JNI注册

Android中注册JNI方法有两种方式,一种是静态注册,一种是动态注册。

动态注册

因为JNI允许我们提供一个函数映射表(native函数和jni函数对应表)。而在执行System.loadLibrary加载so库时,会执行该so的JNI_OnLoad函数,利用这个时机可以动态注册JIN方法。具体实现的话是通过JNINativeMethod结构保存映射关系,然后通过RegisterNatives函数来将该映射关系注册。

静态注册

如果没有在JNI_OnLoad中将JNI方法注册(将方法在进程中的地址增加到ClassObject->directMethods中),则在调用的时候解析javah风格的函数(比如Java_com_example_hellojni_HelloJni_stringFromJNI),进行静态注册。静态注册根据函数名来建立java方法和jni函数的对应关系。静态注册需要根据方法名本地搜索,比较耗时。

RN中的JNI注册
JNI_OnLoad
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# src/main/jni/react/jni/OnLoad.cpp
extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
// 调用各个类的注册方法
return initialize(vm, [] {
gloginit::initialize();
JSCJavaScriptExecutorHolder::registerNatives();
ProxyJavaScriptExecutorHolder::registerNatives();
CatalystInstanceImpl::registerNatives();
CxxModuleWrapperBase::registerNatives();
CxxModuleWrapper::registerNatives();
JCxxCallbackImpl::registerNatives();
NativeArray::registerNatives();
NativeDeltaClient::registerNatives();
ReadableNativeArray::registerNatives();
WritableNativeArray::registerNatives();
NativeMap::registerNatives();
ReadableNativeMap::registerNatives();
WritableNativeMap::registerNatives();
ReadableNativeMapKeySetIterator::registerNatives();

#ifdef WITH_INSPECTOR
JInspector::registerNatives();
#endif
});
}
CatalystInstanceImpl.registerNatives

先拿CatalystInstanceImpl来举例子,先准备好映射数据,然后构建映射表,再通过FindClass获取对应java类的引用,通过这些参数使用RegisterNatives函数来注册。

1
2
3
4
5
6
7
8
9
10
11
# src/main/jni/react/jni/CatalystInstanceImpl.h 
class CatalystInstanceImpl : public jni::HybridClass<CatalystInstanceImpl> {
// 该类对应的Java类,通过该路径来加载java类的class引用
public:
static constexpr auto kJavaDescriptor = "Lcom/facebook/react/bridge/CatalystInstanceImpl;";

static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jclass>);
~CatalystInstanceImpl() override;

static void registerNatives();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# src/main/jni/react/jni/CatalysInstanceImpl.cpp 
void CatalystInstanceImpl::registerNatives() {
registerHybrid({
// 返回{java方法名,java类命名空间(CatalysInstanceImpl.kJavaDescriptor),JNIMethod指针}
makeNativeMethod("initHybrid", CatalystInstanceImpl::initHybrid),
makeNativeMethod("initializeBridge", CatalystInstanceImpl::initializeBridge),
makeNativeMethod("jniExtendNativeModules", CatalystInstanceImpl::extendNativeModules),
makeNativeMethod("jniSetSourceURL", CatalystInstanceImpl::jniSetSourceURL),
makeNativeMethod("jniRegisterSegment", CatalystInstanceImpl::jniRegisterSegment),
makeNativeMethod("jniLoadScriptFromAssets", CatalystInstanceImpl::jniLoadScriptFromAssets),
makeNativeMethod("jniLoadScriptFromFile", CatalystInstanceImpl::jniLoadScriptFromFile),
makeNativeMethod("jniLoadScriptFromDeltaBundle", CatalystInstanceImpl::jniLoadScriptFromDeltaBundle),
makeNativeMethod("jniCallJSFunction", CatalystInstanceImpl::jniCallJSFunction),
makeNativeMethod("jniCallJSCallback", CatalystInstanceImpl::jniCallJSCallback),
makeNativeMethod("setGlobalVariable", CatalystInstanceImpl::setGlobalVariable),
makeNativeMethod("getJavaScriptContext", CatalystInstanceImpl::getJavaScriptContext),
makeNativeMethod("jniHandleMemoryPressure", CatalystInstanceImpl::handleMemoryPressure),
});

JNativeRunnable::registerNatives();
}
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×