React Native是怎麼在Android上跑起來的
(原始碼版本:0.34,新版本(0.48)基本流程是不變的,建議跟著原始碼看看,哪個版本的倒影響不大)
這篇簡單刨析一下React Native
是怎麼在Android
上跑起來的,會從下面幾個方面說說。
- 啟動流程
- 通訊機制
- 事件驅動
- 渲染原理
- 指令碼執行
啟動流程
React Native
在Android
上啟動是從ReactRootView.startReactApplication
觸發的,而ReactRootView
是繼承FrameLayout
的,所以React Native
在Android
的操作都是在這個View
中進行的。
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
上下文物件,然後到
//JavaScriptExecutor 預設就是jsc,如果的debug在chrome上時候,就是v8。
//JSBundleLoader 有AssetLoader FileLoader CachedBundleFromNetworkLoader RemoteDebuggerBundleLoader 從不同的地方載入bundle
private void recreateReactContextInBackground(
JavaScriptExecutor.Factory jsExecutorFactory,
JSBundleLoader jsBundleLoader) {
UiThreadUtil.assertOnUiThread();
ReactContextInitParams initParams =
new ReactContextInitParams(jsExecutorFactory, jsBundleLoader);
if (mReactContextInitAsyncTask == null) {
// No background task to create react context is currently running, create and execute one.
mReactContextInitAsyncTask = new ReactContextInitAsyncTask();
mReactContextInitAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, initParams);
} else {
// Background task is currently running, queue up most recent init params to recreate context
// once task completes.
mPendingReactContextInitParams = initParams;
}
}
主要的建立工作就轉移到了ReactContextInitAsyncTask
這個AsyncTask
裡面,
@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();//NativeModule的登錄檔
JavaScriptModuleRegistry.Builder jsModulesBuilder = new JavaScriptModuleRegistry.Builder();//jsModules的登錄檔
...打包定義的各種modules到上面的登錄檔...
//建立關鍵的CatalystInstanceImpl
CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder()
...
catalystInstance = catalystInstanceBuilder.build();
....
//扔到js執行緒中載入js指令碼
catalystInstance.getReactQueueConfiguration().getJSQueueThread().callOnQueue(
new Callable<Void>() {
@Override
public Void call() throws Exception {
//讓reactContext持有catalystInstance
reactContext.initializeWithInstance(catalystInstance);
...
catalystInstance.runJSBundle();
return null;
}
}).get();
}
在CatalystInstanceImpl
的建構函式中有
...
//native C++方法,用來初始化JNI相關狀態然後返回mHybridData。具體在 OnLoad.cpp 的 JSCJavaScriptExecutorHolder 類中
mHybridData = initHybrid();
...
//初始化執行緒環境,包括和主執行緒繫結,JS執行緒,Native執行緒建立。
mReactQueueConfiguration = ReactQueueConfigurationImpl.create(
ReactQueueConfigurationSpec,
new NativeExceptionHandler());
...
initializeBridge(
new BridgeCallback(this),//CatalystInstanceImpl內部類,用於native對java的一些回撥
jsExecutor,//jsc
mReactQueueConfiguration.getJSQueueThread(),//js執行緒佇列
mReactQueueConfiguration.getNativeModulesQueueThread(),//native執行緒佇列
mJavaRegistry.getModuleRegistryHolder(this));//nativemodules登錄檔
mMainExecutorToken = getMainExecutorToken();//貌似是用於切換jsExecutor的標記,後面版本刪掉了。
然後就進入到了cpp
層的CatalystInstanceImpl.cpp
的initializeBridge
方法
void CatalystInstanceImpl::initializeBridge(
jni::alias_ref<ReactCallback::javaobject> callback,
// This executor is actually a factory holder.
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
方法
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);
//在js執行緒中包裝nativeQueue和建立nativeToJsBridge_,後者在雙向bridge起作用,不要僅僅看名字,內部還有一個JsToNativeBridge
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
程式碼
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();
}
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
方法。看看執行完這麼一大堆準備程式碼之後,是怎麼拉起來整個應用的。
@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);
//真正拉起react native 的地方
catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams);
}
再來詳細說一下最後一句,(大量程式碼預警)
@Override
public <T extends JavaScriptModule> T getJSModule(ExecutorToken executorToken, Class<T> jsInterface) {
//進入JSModuleRegistry中
return Assertions.assertNotNull(mJSModuleRegistry)
.getJavaScriptModule(this, executorToken, jsInterface);
}
//JavaScriptModuleRegistry.java
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 =
...
//很明顯 動態代理 重點關注JavaScriptModuleInvocationHandler的invoke方法
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 {
....
//又跑到了CatalystInstanceImpl.java中。。。然後又橋接到了CatalystInstanceImpl.cpp中,同樣會呼叫instance的對應方法,直接看吧
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));
}
//又會進入executor->callFunction(module, method, arguments);
void JSCExecutor::callFunction(const std::string& moduleId, const std::string& methodId, const folly::dynamic& arguments) {
....
auto result = [&] {
try {
//被橋接到MessageQueue.js的callFunctionReturnFlushedQueue方法
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));
}
}();
//順便還會呼叫一下native的 這個會在後面再說一下
callNativeModules(std::move(result));
}
callFunctionReturnFlushedQueue(module, method, args) {
guard(() => {
//執行js的function
this.__callFunction(module, method, args);
this.__callImmediates();
});
//取出積攢在queue中的action返回給上面的,最終在java中執行
return this.flushedQueue();
}
__callFunction(module: string, method: string, args: any) {
...
//根據module名,方法名和引數執行js方法
const result = moduleMethods[method].apply(moduleMethods, args);
return result;
}
//那什麼時候把js的module註冊到moduleMethods中呢
//AppRegistry.js
BatchedBridge.registerCallableModule(
'AppRegistry',
AppRegistry
);
//BatchedBridge是啥?
const BatchedBridge = new MessageQueue(
() => global.__fbBatchedBridgeConfig,
serializeNativeParams
);
registerCallableModule(name, methods) {
this._callableModules[name] = methods;
}
這裡就執行了AppRegistry.js
的的runApplication
方法。
runApplication: function(appKey: string, appParameters: any): void {
...
runnables[appKey].run(appParameters);
},
//而runnables是在什麼時候被新增的??下面
registerComponent: function(appKey: string, getComponentFunc: ComponentProvider): string {
runnables[appKey] = {
run: (appParameters) =>
renderApplication(getComponentFunc(), appParameters.initialProps, appParameters.rootTag)
};
return appKey;
},
//而registerComponent什麼時候被呼叫的就不用說了吧
到此真正執行到了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
的呼叫會在渲染原理裡面提到。
簡單畫了個圖
渲染原理
現在以一個Image
如何渲染到Native
為例,說一下簡單的流程。
當執行js
的指令碼時候,是不知道nativeModule
的登錄檔的,因為nativeModule
的登錄檔只儲存在java
和cpp
端,並沒有直接傳遞到js
端。所有當執行到
import {
Image,
} from 'react-native';
這時候js
並不知道Image
是什麼,然後看一下程式碼
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
,其他
呼叫型別,最終都是呼叫了
__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
方法中可以看到
appParams.putDouble("rootTag", rootTag);
appParams.putMap("initialProps", initialProps);
把rootTag
通過bridge
帶到了js
端,js
執行React
邏輯後,要建立一個Native
的View
,同時也把這個rootTag
帶到java
層,讓java
層知道,建立完一個View
要新增到哪個根佈局上。
這個rootTag
的生成是有規則的,在UIManagerModule.addMeasuredRootView
的時候會生成RootViewTag
,
final int tag = mNextRootViewTag;//預設是1
mNextRootViewTag += ROOT_VIEW_TAG_INCREMENT;//10
也就是預設的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
ReactNative.render(
<AppContainer>
<RootComponent
{...initialProps}
rootTag={rootTag}
/>
</AppContainer>,
rootTag
);
這裡的AppContainer
也是一個元件,是包裹在根佈局的外面,用於debug
的紅盒等工具佈局。再到了
//ReactNative.js
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
,根據上面的提示是可以跟到這裡的。只挑簡單的看,看這個方法裡面的
var tag = ReactNativeTagHandles.allocateTag();//給每個view生成一個唯一的tag
...
UIManager.createView(tag, this.viewConfig.uiViewClassName, nativeTopRootTag, updatePayload);
//ReactNativeTagHandles.js
allocateTag: function () {
//排除已經給分配給rootTag的 類似1,11,21
//下面的就是簡單的自增,初始化是1
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
@ReactMethod
//建立view的tag,對應native的元件類名,要加入的根佈局tag,建立view需要的引數
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
方法
...
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
,這裡面主要是
mEventStaging.add(event);//ArrayList<Event>
把事件新增到一個待發送的列表裡面。那什麼是去處傳送?是在ScheduleDispatchFrameCallback.doFrame
,
@Override
public void doFrame(long frameTimeNanos) {
....
moveStagedEventsToDispatchQueue();
...
mReactContext.runOnJSQueueThread(mDispatchEventsRunnable);
}
呼叫moveStagedEventsToDispatchQueue
在這個方法裡面會對event
再做一些處理,例如壓縮,合併事件等,然後又把處理完的事件放到Event[] mEventsToDispatch = new Event[16];
中。而在DispatchEventsRunnable
的run
方法中
@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引擎
。。
最後再貼一下簡單畫的思維導圖吧
參考:
ps:
因為本人能力實在有限,上面很多都是連蒙帶猜,算是個筆記性質的流水賬,有用就看看,沒用就算了,歡迎指出錯誤。
pps
這篇本該在兩星期之前完成的工作,一直拖到了現在。(一是因為懶),二是因為不知道該怎麼更好的表述出來,因為一直貼程式碼體驗實在是不好。(雖然現在還是這樣的,但是原始碼分析的不貼程式碼怎麼寫)。但是感覺再不寫點出來,過段時間又忘了,索性寫出來算了,也不管效果了。。。湊合看吧。