(源码版本:0.34,新版本(0.48)基本流程是不变的,建议跟着源码看看,哪个版本的倒影响不大) 这篇简单刨析一下React Native
是怎么在Android
上跑起来的,会从下面几个方面说说。
启动流程 React Native
在Android
上启动是从ReactRootView.startReactApplication
触发的,而ReactRootView
是继承FrameLayout
的,所以React Native
在Android
的操作都是在这个View
中进行的。
1
startReactApplication(ReactInstanceManager reactInstanceManager, String moduleName, @Nullable Bundle launchOptions)
这个方法参数第一个ReactInstanceManager
,实现是XReactInstanceManagerImpl
,可以理解在应用层对RN
的配置都是对这个类操作实现的。moduleName
是要启动的RN
的Component
的name
,是在js
的AppRegistry.registerComponent('xxx', () => App);
定义的。最后的launchOptions
是传过去的参数,可以在js
的Component
的props
中获取。
下一步到了mReactInstanceManager.createReactContextInBackground();
是在后台线程中创建RN
的ReactContext
上下文对象,然后到1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private void recreateReactContextInBackground (
JavaScriptExecutor.Factory jsExecutorFactory,
JSBundleLoader jsBundleLoader) {
UiThreadUtil.assertOnUiThread();
ReactContextInitParams initParams =
new ReactContextInitParams(jsExecutorFactory, jsBundleLoader);
if (mReactContextInitAsyncTask == null ) {
mReactContextInitAsyncTask = new ReactContextInitAsyncTask();
mReactContextInitAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, initParams);
} else {
mPendingReactContextInitParams = initParams;
}
}
主要的创建工作就转移到了ReactContextInitAsyncTask
这个AsyncTask
里面,
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
27
28
29
30
31
32
33
@Override
protected Result<ReactApplicationContext> doInBackground (ReactContextInitParams... params) {
....
return Result.of(createReactContext(jsExecutor, params[0 ].getJsBundleLoader()));
....
}
private ReactApplicationContext createReactContext (
JavaScriptExecutor jsExecutor,
JSBundleLoader jsBundleLoader) {
...
NativeModuleRegistry.Builder nativeRegistryBuilder = new NativeModuleRegistry.Builder();
JavaScriptModuleRegistry.Builder jsModulesBuilder = new JavaScriptModuleRegistry.Builder();
...打包定义的各种modules到上面的注册表...
CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder()
...
catalystInstance = catalystInstanceBuilder.build();
....
catalystInstance.getReactQueueConfiguration().getJSQueueThread().callOnQueue(
new Callable<Void>() {
@Override
public Void call () throws Exception {
reactContext.initializeWithInstance(catalystInstance);
...
catalystInstance.runJSBundle();
return null ;
}
}).get();
}
在CatalystInstanceImpl
的构造函数中有
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
...
mHybridData = initHybrid();
...
mReactQueueConfiguration = ReactQueueConfigurationImpl.create(
ReactQueueConfigurationSpec,
new NativeExceptionHandler());
...
initializeBridge(
new BridgeCallback(this ),
jsExecutor,
mReactQueueConfiguration.getJSQueueThread(),
mReactQueueConfiguration.getNativeModulesQueueThread(),
mJavaRegistry.getModuleRegistryHolder(this ));
mMainExecutorToken = getMainExecutorToken();
然后就进入到了cpp
层的CatalystInstanceImpl.cpp
的initializeBridge
方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
void CatalystInstanceImpl::initializeBridge(
jni::alias_ref<ReactCallback::javaobject> callback,
JavaScriptExecutorHolder* jseh,
jni::alias_ref<JavaMessageQueueThread::javaobject> jsQueue,
jni::alias_ref<JavaMessageQueueThread::javaobject> moduleQueue,
ModuleRegistryHolder* mrh) {
instance_->initializeBridge(folly::make_unique<JInstanceCallback>(callback),
jseh->getExecutorFactory(),
folly::make_unique<JMessageQueueThread>(jsQueue),
folly::make_unique<JMessageQueueThread>(moduleQueue),
mrh->getModuleRegistry());
}
然后有委托给了Instance.cpp
的initializeBridge
方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void Instance::initializeBridge(
std ::unique_ptr <InstanceCallback> callback,
std ::shared_ptr <JSExecutorFactory> jsef,
std ::shared_ptr <MessageQueueThread> jsQueue,
std ::unique_ptr <MessageQueueThread> nativeQueue,
std ::shared_ptr <ModuleRegistry> moduleRegistry) {
callback_ = std ::move(callback);
jsQueue->runOnQueueSync(
[this , &jsef, moduleRegistry, jsQueue,
nativeQueue=folly::makeMoveWrapper(std ::move(nativeQueue))] () mutable {
nativeToJsBridge_ = folly::make_unique<NativeToJsBridge>(
jsef.get(), moduleRegistry, jsQueue, nativeQueue.move(), callback_);
});
CHECK(nativeToJsBridge_);
}
到这就看没了,再回到上面的catalystInstance.runJSBundle();
以FileLoader
为例,最终走到native void loadScriptFromFile(String fileName, String sourceURL);
进入CatalystInstanceImpl.cpp
进而委托给Instance.cpp
。预警。。下面是一大片的cpp
代码
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
void Instance::loadScriptFromFile(const std::string& filename,
const std::string& sourceURL) {
...检测文件合法性等...
loadScriptFromString(std::move(buf), sourceURL);
}
void Instance::loadScriptFromString(std::unique_ptr<const JSBigString> string,
std::string sourceURL) {
callback_->incrementPendingJSCalls();//这个callback就是java层的CatalystInstanceImpl的BridgeCallback这个内部类。
...
nativeToJsBridge_->loadApplicationScript(std::move(string), std::move(sourceURL));
}
void NativeToJsBridge::loadApplicationScript(std::unique_ptr<const JSBigString> script,
std::string sourceURL) {
m_mainExecutor->loadApplicationScript(std::move(script), std::move(sourceURL));
}
void JSCExecutor::loadApplicationScript(std::unique_ptr<const JSBigString> script, std::string sourceURL) throw(JSException) {
...
//使用webkit JSC去真正解释执行Javascript了!
evaluateScript(m_context, jsScript, jsSourceURL);
//绑定桥,核心是通过getGlobalObject将JS与C++通过webkit JSC bind
bindBridge();
flush();//这里算是通知java,加载完js脚本
}
void JSCExecutor::bindBridge() throw(JSException) {
...下面都是通过jsc 获取js的一下属性,方法等...
auto global = Object::getGlobalObject(m_context);
auto batchedBridgeValue = global.getProperty("__fbBatchedBridge");
...
auto batchedBridge = batchedBridgeValue.asObject();
m_callFunctionReturnFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnFlushedQueue").asObject();
m_invokeCallbackAndReturnFlushedQueueJS = batchedBridge.getProperty("invokeCallbackAndReturnFlushedQueue").asObject();
//这个比较重要 获取MessageQueue.js的flushedQueue 下面就用到
m_flushedQueueJS = batchedBridge.getProperty("flushedQueue").asObject();
}
//这个下面js->native的时候还会提到
void JSCExecutor::flush() {
...真的烦,绕来绕去 m_flushedQueueJS看上面
callNativeModules(m_flushedQueueJS->callAsFunction({}));
}
void JSCExecutor::callNativeModules(Value&& value) {
...
try {
auto calls = value.toJSONString();
//class JsToNativeBridge : public react::ExecutorDelegate
m_delegate->callNativeModules(*this, std::move(calls), true);
} catch (...) {
...
}
}
void callNativeModules(
JSExecutor& executor, std::string callJSON, bool isEndOfBatch) override {
ExecutorToken token = m_nativeToJs->getTokenForExecutor(executor);
m_nativeQueue->runOnQueue([this, token, callJSON=std::move(callJSON), isEndOfBatch] {
for (auto& call : react::parseMethodCalls(callJSON)) {
//快完了 这个是ModuleRegistry.cpp 是在initializeBridge间接创建包装nativemodule的
m_registry->callNativeMethod(
token, call.moduleId, call.methodId, std::move(call.arguments), call.callId);
}
if (isEndOfBatch) {
//又见到了这个callback
m_callback->onBatchComplete();
m_callback->decrementPendingJSCalls();
}
});
}
void ModuleRegistry::callNativeMethod(ExecutorToken token, unsigned int moduleId, unsigned int methodId,
folly::dynamic&& params, int callId) {
...
modules_[moduleId]->invoke(token, methodId, std::move(params));
}
看到最后一句就是要去调用nativeModule
里面的方法了,具体在ModuleRegistryHolder.cpp
的JavaNativeModule
类和NewJavaNativeModule
类,对应Java
的JavaModuleWrapper.java
,就是jni
调用。
说到这里,现在只完成了bridge
环境的初步搭建,把jsbundle
扔到jsc
里面,还没真正拉起React Native
应用。还是回到上面那个AsyncTask
的onPostExecute
方法。看看执行完这么一大堆准备代码之后,是怎么拉起来整个应用的。
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
27
28
29
30
@Override
protected void onPostExecute (Result<ReactApplicationContext> result) {
....
setupReactContext(result.get());
}
private void setupReactContext (ReactApplicationContext reactContext) {
...各种listener回调,通知birdge就绪,reactContext创建完成
for (ReactRootView rootView : mAttachedRootViews) {
attachMeasuredRootViewToInstance(rootView, catalystInstance);
}
...各种listener回调,通知birdge就绪,reactContext创建完成
}
private void attachMeasuredRootViewToInstance (ReactRootView rootView,CatalystInstance catalystInstance) {
....
UIManagerModule uiManagerModule = catalystInstance.getNativeModule(UIManagerModule.class);
int rootTag = uiManagerModule.addMeasuredRootView(rootView);
rootView.setRootViewTag(rootTag);
@Nullable Bundle launchOptions = rootView.getLaunchOptions();
WritableMap initialProps = Arguments.makeNativeMap(launchOptions);
String jsAppModuleName = rootView.getJSModuleName();
WritableNativeMap appParams = new WritableNativeMap();
appParams.putDouble("rootTag" , rootTag);
appParams.putMap("initialProps" , initialProps);
catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams);
}
再来详细说一下最后一句,(大量代码预警)
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
@Override
public <T extends JavaScriptModule> T getJSModule (ExecutorToken executorToken, Class<T> jsInterface) {
return Assertions.assertNotNull(mJSModuleRegistry)
.getJavaScriptModule(this , executorToken, jsInterface);
}
public synchronized <T extends JavaScriptModule> T getJavaScriptModule (
CatalystInstance instance,
ExecutorToken executorToken,
Class<T> moduleInterface) {
HashMap<Class<? extends JavaScriptModule>, JavaScriptModule> instancesForContext =
mModuleInstances.get(executorToken);
if (instancesForContext == null ) {
instancesForContext = new HashMap<>();
mModuleInstances.put(executorToken, instancesForContext);
}
JavaScriptModule module = instancesForContext.get(moduleInterface);
if (module != null ) {
return (T) module ;
}
JavaScriptModuleRegistration registration =
...
JavaScriptModule interfaceProxy = (JavaScriptModule) Proxy.newProxyInstance(
moduleInterface.getClassLoader(),
new Class[]{moduleInterface},
new JavaScriptModuleInvocationHandler(executorToken, instance, registration));
instancesForContext.put(moduleInterface, interfaceProxy);
return (T) interfaceProxy;
}
@Override
public @Nullable Object invoke (Object proxy, Method method, @Nullable Object[] args) throws Throwable {
....
mCatalystInstance.callFunction(
executorToken,
mModuleRegistration.getName(),
method.getName(),
jsArgs
);
return null ;
}
void Instance::callJSFunction(ExecutorToken token, std::string&& module , std::string&& method,
folly::dynamic&& params) {
callback_->incrementPendingJSCalls();
nativeToJsBridge_->callFunction(token, std::move(module ), std::move(method), std::move(params));
}
void JSCExecutor::callFunction(const std::string& moduleId, const std::string& methodId, const folly::dynamic& arguments) {
....
auto result = [&] {
try {
return m_callFunctionReturnFlushedQueueJS->callAsFunction({
Value(m_context, String::createExpectingAscii(moduleId)),
Value(m_context, String::createExpectingAscii(methodId)),
Value::fromDynamic(m_context, std::move(arguments))
});
} catch (...) {
std::throw_with_nested(
std::runtime_error("Error calling function: " + moduleId + ":" + methodId));
}
}();
callNativeModules(std::move(result));
}
callFunctionReturnFlushedQueue(module , method, args) {
guard(() => {
this .__callFunction(module , method, args);
this .__callImmediates();
});
return this .flushedQueue();
}
__callFunction(module : string, method: string, args: any) {
...
const result = moduleMethods[method].apply(moduleMethods, args);
return result;
}
BatchedBridge.registerCallableModule(
'AppRegistry' ,
AppRegistry
);
const BatchedBridge = new MessageQueue(
() => global.__fbBatchedBridgeConfig,
serializeNativeParams
);
registerCallableModule(name, methods) {
this ._callableModules[name] = methods;
}
这里就执行了AppRegistry.js
的的runApplication
方法。1
2
3
4
5
6
7
8
9
10
11
12
13
14
runApplication: function (appKey: string, appParameters: any ): void {
...
runnables[appKey].run(appParameters);
},
registerComponent: function (appKey: string, getComponentFunc: ComponentProvider ): string {
runnables[appKey] = {
run : (appParameters ) =>
renderApplication(getComponentFunc(), appParameters.initialProps, appParameters.rootTag)
};
return appKey;
},
到此真正执行到了js
脚本,开始执行Component
的逻辑渲染,最终映射到Native
的View
上。后面会再详细说渲染的原理。同时会发现在 JSCExecutor
中每次 Java
调用 JS
之后会进行 Java
端的一个回调(从 JS
层的 MessageQueue.js
中获得累积的 JS Call
)。
通信机制 上面关于java
->js
已经体现的差不多了,实质就是 Java
与 JS
端都准备好一个 Module
映射表,然后当 Java
端调用 JS
代码时 Java
端通过查表动态代理创建一个与 JS 对应的 Module
对象,当调用这个 Module
的方法时 Java 端通过动态代理的 invoke
方法触发 C++
层,层层调用后通过 JSCExecutor
执行 JS
端队列中的映射查表找到 JS
端方法进行调用;js
->java
的调用会在渲染原理里面提到。
简单画了个图
btw:看完js->java原理之后,会发现其实所谓的双向通信,其实基本上(除了那个直接调用的java之外)都是java
调用。这也体现了消息机制的两种实现方式,推和拉。推方式就是直接调用对应的方法,也可以理解为经典的观察者模式。而RN通信更像拉这种模式。双边的通信都是靠java
端去拉队列中的action
。拉比推有一个优势,就是可以解决背压的问题,不必去解决或者协调生产者生产事件的速度,而是根据消费者的速度去消费事件,而且这个消费的过程还是可以优化的,例如去压缩合并一些事件,一次执行。
渲染原理 现在以一个Image
如何渲染到Native
为例,说一下简单的流程。 当执行js
的脚本时候,是不知道nativeModule
的注册表的,因为nativeModule
的注册表只保存在java
和cpp
端,并没有直接传递到js
端。所有当执行到
1
2
3
import {
Image,
} from 'react-native' ;
这时候js
并不知道Image
是什么,然后看一下代码
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
const ReactNative = {
...
get Image() { return require('Image'); },
...
}
...
module.exports = ReactNative;
//Image.android.js
var NativeModules = require('NativeModules');
//NativeModules.js
const NativeModules = {};
Object.keys(RemoteModules).forEach((moduleName) => {
Object.defineProperty(NativeModules, moduleName, {
configurable: true,
enumerable: true,
get: () => {
let module = RemoteModules[moduleName];
if (module && typeof module.moduleID === 'number' && global.nativeRequireModuleConfig) {
//nativeRequireModuleConfig映射到JSCExecutor.cpp
const config = global.nativeRequireModuleConfig(moduleName);
module = config && BatchedBridge.processModuleConfig(config, module.moduleID);
RemoteModules[moduleName] = module;
}
Object.defineProperty(NativeModules, moduleName, {
configurable: true,
enumerable: true,
value: module,
});
return module;
},
});
});
module.exports = NativeModules;
//cpp
JSCExecutor::nativeRequireModuleConfig->JsToNativeBridge::getModuleConfig->ModuleRegistry::getConfig
folly::dynamic ModuleRegistry::getConfig(const std::string& name) {
...
NativeModule* module = modules_[it->second].get();
...
//最终反射调用JavaModuleWrapper.java的getConstants
folly::dynamic constants = module->getConstants();
...
//最终反射调用JavaModuleWrapper.java的getMethods
//返回对应module中所有@ReactMethod注解的方法
std::vector<MethodDescriptor> methods = module->getMethods();
//modules_在哪赋值?
//ModuleRegistryHolder.cpp构造函数,这个类上面有提到,回去看看
//registry_ = std::make_shared<ModuleRegistry>(std::move(modules));
}
然后返回到NativeModules.js
中,BatchedBridge.processModuleConfig
->_genModule
->_genMethod
。进一步处理一下。所以到现在,js
获取到了Image
这个module
中所有方法和属性。
然后当调用Image
中相关方法时候,其实就是调用上面_genMethod
中的方法,在这个方法中,分promise
,sync
,其他
调用类型,最终都是调用了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
__nativeCall(module, method, params, onFail, onSucc) {
...
this._queue[MODULE_IDS].push(module);
this._queue[METHOD_IDS].push(method);
this._queue[PARAMS].push(preparedParams);
...
//如果5ms内有多个方法调用就先待在队列里防止过高频率,否则调用C++的nativeFlushQueueImmediate方法
if (global.nativeFlushQueueImmediate &&
now - this._lastFlush >= MIN_TIME_BETWEEN_FLUSHES_MS) {
global.nativeFlushQueueImmediate(this._queue);
this._queue = [[], [], [], this._callID];
this._lastFlush = now;
}
}
上面把MODULE_IDS
,METHOD_IDS
,PARAMS
放到queue
中,等待java
的调用,至于什么时候会触发java
的调用和为什么要这么设计,会在下面的事件驱动解释。调用JSCExecutor::flush()
。还有就是直接调用cpp
的nativeFlushQueueImmediate
,最终这两种方式都是调用了callNativeModules
,这个上面也说了,不再赘述啦。
下面再说一下Native
的view
创建过程,这个过程中View
的tag
起标记View
的作用,从java
拉起React Native
的attachMeasuredRootViewToInstance
方法中可以看到
1
2
appParams.putDouble("rootTag" , rootTag);
appParams.putMap("initialProps" , initialProps);
把rootTag
通过bridge
带到了js
端,js
执行React
逻辑后,要创建一个Native
的View
,同时也把这个rootTag
带到java
层,让java
层知道,创建完一个View
要添加到哪个根布局上。
这个rootTag
的生成是有规则的,在UIManagerModule.addMeasuredRootView
的时候会生成RootViewTag
,
1
2
final int tag = mNextRootViewTag;
mNextRootViewTag += ROOT_VIEW_TAG_INCREMENT;
也就是默认的rootTag
是1,后面每多创建一个+10,也就是类似1,11,21
这样都是根布局的tag
。
再通过这个rootTag
在js
的传递简单说一下React.js
的创建组件逻辑。从前面可以知道,拉起js
后执行AppRegistry.js ::runApplication
,进而执行到了renderApplication(getComponentFunc(), appParameters.initialProps, appParameters.rootTag)
这个方法。这里可以看到从java
传过来的两个参数,其中一个就是rootTag
,这里默认就一个根布局,这里的rootTag==1
,进而到了renderApplication.js
1
2
3
4
5
6
7
8
9
ReactNative.render(
<AppContainer>
<RootComponent
{...initialProps}
rootTag={rootTag}
/>
</AppContainer>,
rootTag
);
这里的AppContainer
也是一个组件,是包裹在根布局的外面,用于debug
的红盒等工具布局。再到了
1
2
3
4
var render = function (element, mountInto, callback ) {
return ReactNativeMount.renderComponent(element, mountInto, callback);
};
这里面的逻辑快到React
的一些处理,这里不多赘述,其实还有很多关于React Native
的处理,暂时忽略,分支太多太繁琐。简单说一下React Native
组件可以分为两种
元组件:框架内置的,可以直接使用的组件。例如:View、Image等。它在React Native中用ReactNativeBaseComponent来描述。 复合组件:用户封装的组件,一般可以通过React.createClass()来构建,提供render()方法来返回渲染目标。它在React Native中用ReactCompositeComponent来描述。
具体组合的逻辑基本都在上面连个类里面。下面来到ReactNativeBaseComponent.js
的mountComponent
,根据上面的提示是可以跟到这里的。只挑简单的看,看这个方法里面的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var tag = ReactNativeTagHandles.allocateTag();
...
UIManager.createView(tag, this .viewConfig.uiViewClassName, nativeTopRootTag, updatePayload);
allocateTag: function () {
while (this .reactTagIsNativeTopRootID(ReactNativeTagHandles.tagCount)) {
ReactNativeTagHandles.tagCount++;
}
var tag = ReactNativeTagHandles.tagCount;
ReactNativeTagHandles.tagCount++;
return tag;
},
看名字也知道这里就到了创建View
的地方,还有另外两个方法和这个差不多的,用来操作View
,分别的updateView
,manageChildren
,UIManager
通过bridge
可以映射到java
的UIManagerModule.java
,可以在duiyiing这个类里面找到对应的用@ReactMethod
注解的方法,这个注解是干啥的,看上面有提到。这里只看createView
1
2
3
4
5
@ReactMethod
public void createView (int tag, String className, int rootViewTag, ReadableMap props) {
mUIImplementation.createView(tag, className, rootViewTag, props);
}
在UIImplementation.java
中把要创建的view
包装成CSSNode
,用于后面的在CssLayout
中布局。然后会包装成一个CreateViewOperation
加入到UIViewOperationQueue.java
的ArrayDeque<UIOperation> mNonBatchedOperations
这个队列中。最后还是通过GuardedChoreographerFrameCallback
这个垂直同步的回调中出队,执行。关于事件驱动还是看下面。还有 updateview
setchilderen
就不说了,很复杂。
事件驱动 在说React Native
的事件驱动之前,先看一下这几篇Android图形显示系统(一) React Native 分析(二)事件驱动 Android中的SurfaceFlinger和Choreographer 了解一下垂直同步和在Android
上的Choreographer
,正因为React Native
使用了Choreographer
这个类,而这个类是在4.1加入的,所以RN-Android
的最低兼容是4.1,而weex
是最低兼容到4.0,是在4.0使用了handler
延时来模拟垂直同步的效果。当然这也是老版本Android
的做法。这也是为啥总是吐槽Android
显得很卡,当然在5.0又引入了renderThread
就更上了一个台阶,还有Android
的属性动画也是靠这个驱动的。
下面简单贴一下Choreographer
的注释,看看为啥跨平台的框架都会用到这个类
However, there are a few cases where you might want to use the functions of thechoreographer directly in your application. Here are some examples.
If your application does its rendering in a different thread, possibly using GL,or does not use the animation framework or view hierarchy at all and you want to ensure that it is appropriately synchronized with the display, then use {@link Choreographer#postFrameCallback}. … and that’s about it. Each {@link Looper} thread has its own choreographer. Other threads can post callbacks to run on the choreographer but they will run on the {@link Looper}to which the choreographer belongs.
再看一下postFrameCallback
注释
Posts a frame callback to run on the next frame.The callback runs once then is automatically removed.
在React Native
的使用主要在EventDispatcher
的内部类private class ScheduleDispatchFrameCallback implements Choreographer.FrameCallback
和ReactChoreographer
与它的内部类private class ReactChoreographerDispatcher implements Choreographer.FrameCallback
,还有用于view
或者动画的就不说了。
现在举个例子,点击一下view
,这个事件是怎么传递的,点击事件肯定发生在java
端。在ReactRootView
的dispatchJSTouchEvent
方法
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
...
EventDispatcher eventDispatcher = reactContext.getNativeModule(UIManagerModule.class)
.getEventDispatcher();
mJSTouchDispatcher.handleTouchEvent(event, eventDispatcher);
//JSTouchDispatcher.java
public void handleTouchEvent(MotionEvent ev, EventDispatcher eventDispatcher) {
//这里面分为down,up move 等事件类别
mTargetTag = TouchTargetHelper.findTargetTagAndCoordinatesForTouch(
ev.getX(),
ev.getY(),
mRootViewGroup,
mTargetCoordinates,
null);
eventDispatcher.dispatchEvent(
TouchEvent.obtain(
mTargetTag,
TouchEventType.START,
ev,
mGestureStartTime,
mTargetCoordinates[0],
mTargetCoordinates[1],
mTouchEventCoalescingKeyHelper));
}
最终包装成一个TouchEvent
调用eventDispatcher.dispatchEvent
,这里面主要是
1
mEventStaging.add(event);
把事件添加到一个待发送的列表里面。那什么是去处发送?是在ScheduleDispatchFrameCallback.doFrame
,
1
2
3
4
5
6
7
8
@Override
public void doFrame (long frameTimeNanos) {
....
moveStagedEventsToDispatchQueue();
...
mReactContext.runOnJSQueueThread(mDispatchEventsRunnable);
}
调用moveStagedEventsToDispatchQueue
在这个方法里面会对event
再做一些处理,例如压缩,合并事件等,然后又把处理完的事件放到Event[] mEventsToDispatch = new Event[16];
中。而在DispatchEventsRunnable
的run
方法中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Override
public void run () {
for (int eventIdx = 0 ; eventIdx < mEventsToDispatchSize; eventIdx++) {
Event event = mEventsToDispatch[eventIdx];
....
event.dispatch(mRCTEventEmitter);
event.dispose();
...
}
}
->TouchEvent.dispatch->TouchesHelper.sendTouchEvent->rctEventEmitter.receiveTouches(
type.getJSEventName(),
pointers,
changedIndices);
而RCTEventEmitter extends JavaScriptModule
这个就是走上面的java->js
的路子,动态代理->cpp
->flush()
->….
简单点就是getJSModule
后对js的方法调用都会触发上面MessageQueue.js
的出队
脚本执行 这里简单说说React Native
的js引擎
选择,都是webkit
的JSC
,在iOS
上是内置的,在Android
上则是引入了一个完整的JSC
,这也是为什么Android
的RN
会大这么多的很重要的原因,至于为什么要引入一个完整的JSC
而不是使用内置的js
引擎,Android 4.4
之前的android
系统浏览器内核是WebKit
,Android4.4
系统浏览器切换到了Chromium
(内核是Webkit
的分支Blink
)。在Android
平台已经启用V8作为JS引擎,Android 4.0
以后只用到了JavaScriptCore
中的WTF(Web Template Library)部分代码。
至于为啥不都使用V8
,这个都是iOS
的锅,看看chrome
在iOS
上就是个WebView
套个壳。。。
还有其他的跨平台框架,例如weex
,在Android
上使用的是V8
。现在网上也有对RN
在Android
上移植的V8
版本。onesubone/react-native-android-v8 React Native Android V8接入 这个是基于0.46的版本,还是可以跑起来的,但是RN
的速度瓶颈貌似并不在js引擎
。。
还有一点要吐槽就是每个jsbundle
都包含了框架代码和业务代码,导致文件的有效利用率很低,看看隔壁的weex
就做的很好,框架js
代码直接包含在sdk
中,只下发业务代码,这才是正常的做法。在RN
这只能自己hook
了。还有就是每个RN
页面都是要重新初始化Bridge,但是这些bridge其实都是基本一样的,只需要把不同的jsbundle
扔给JSC
就好了,我测试了一下大概会快20%,但是因为缓存,也会造成一些内存泄露的问题(业务代码和具体页面过于绑定)。但是有时候甚至比重新初始化还慢,不知道为啥。。
最后再贴一下简单画的思维导图吧思维导图在线地址
参考:
facebook/react-native React Native Android 源码框架浅析(主流程及 Java 与 JS 双边通信) ReactNative源码篇 吟游雪人
ps: 因为本人能力实在有限,上面很多都是连蒙带猜,算是个笔记性质的流水账,有用就看看,没用就算了,欢迎指出错误。
pps 这篇本该在两星期之前完成的工作,一直拖到了现在。(一是因为懒),二是因为不知道该怎么更好的表述出来,因为一直贴代码体验实在是不好。(虽然现在还是这样的,但是源码分析的不贴代码怎么写)。但是感觉再不写点出来,过段时间又忘了,索性写出来算了,也不管效果了。。。凑合看吧。