編輯:關於Android編程
前一篇博客分析了Native端向Javascript端通信的全流程,這次來研究下Javascript端向Native端通信的全流程,與前篇恰好構成一個基本完整的通信機制。
本篇博客內容與前篇聯系較大,有些分析過的東西這次就直接拿來用了,不再贅述,所以希望閱讀這篇文章之前先熟悉下前篇:
React-Native系列Android——Native與Javascript通信原理(一)
引用下前篇中的通信模型:
Native<喎?/kf/ware/vc/" target="_blank" class="keylink">vc3Ryb25nPtPrPHN0cm9uZz5KYXZhc2NyaXB0PC9zdHJvbmc+1q685LXEy6vP8s2o0MXG5Mq1ysfE48C0ztLN+bXE0ru49tGtu7e5/bPMo6y+zbrDz/HKx8G9uPbIy9TattS7sKOsxOPSu77kztLSu77kyLu688Tj1NnSu77kztLU2dK7vuSho8THw7TX3NPQ0ru49rvhu7C1xLeixvDV37DJo7+1sci7ysc8c3Ryb25nPk5hdGl2ZTwvc3Ryb25nPsHLo6zS8s6qy/nT0LXE0NDOqra8yse00zxzdHJvbmc+TmF0aXZlPC9zdHJvbmc+tsu3osbwtcSjrNPDu6ey2df31rG908Pmz/K1xNKyysc8c3Ryb25nPk5hdGl2ZTwvc3Ryb25nPqGjy/nS1NXiuPbNqNDFxKPQzdPWv8nS1L+0s8nKxzxzdHJvbmc+TmF0aXZlPC9zdHJvbmc+t6LG8Lvhu7CjrMi7uvM8c3Ryb25nPkphdmFzY3JpcHQ8L3N0cm9uZz69+NDQ06a08KGjPC9wPg0KPHA+y/nS1KOsvfHM7LXEsqnOxNbYteO+zcrHt9bO9jxzdHJvbmc+SmF2YXNjcmlwdDwvc3Ryb25nPsrHyOe6ztOmtPA8c3Ryb25nPk5hdGl2ZTwvc3Ryb25nPqOszazKsTxzdHJvbmc+TmF0aXZlPC9zdHJvbmc+09bKx8jnus60psDtwLTX1DxzdHJvbmc+SmF2YXNjcmlwdDwvc3Ryb25nPrXE06a08LXEoaM8L3A+DQo8aHIgLz4NCjxwPjxzdHJvbmc+MaGiSmF2YXNjcmlwdLXE06a08Dwvc3Ryb25nPjwvcD4NCjxwPru5vMe1w8ewxqo8c3Ryb25nPkJyaWRnZTwvc3Ryb25nPrLj1cK92tbQPHN0cm9uZz5KU0NFeGVjdXRvcjo6Y2FsbEZ1bmN0aW9uPC9zdHJvbmc+1+6689PQ0ru49jxzdHJvbmc+Y2FsbE5hdGl2ZU1vZHVsZXM8L3N0cm9uZz61xLX308PC8KO/zfy8x8HLsrvSqr30o6zU2cC0u9i5y8/CPHN0cm9uZz5qbmkvcmVhY3QvSlNDRXhlY3V0b3IuY3BwPC9zdHJvbmc+1tC1xNXits60+sLrsMmjoTwvcD4NCjxwcmUgY2xhc3M9"brush:java;">
executeJSCallWithJSC方法執行Javascript最終腳本後返回了一個名為calls的JSON串,這個字符串又被塞進了Bridge.cpp的callNativeModules方法,這個方法大家都能根據字面意思猜測到是調用Native端組件的,那麼這個JSON串內容就是來自Javascript的應答內容了。 callNativeModules方法及裡面的細節我們暫時放一放,先來看看這個Javascript的應答內容裡面到底是些神馬東西! 前篇裡面,被WebKit庫執行的Javascript語句,大家應該還記得吧,最終如下: 再次看一下MessageQueue.js的callFunctionReturnFlushedQueue方法內容: 返回的是flushedQueue()方法,而flushedQueue()返回的是this._queue數組或者null。 這裡有個小細節,flushedQueue()並不是直接返回this._queue的,而是新定義了一個局部變量queue,先將this._queue的值賦給queue用於返回,然後又清空數組內容。這種處理方式,說明了this._queue數組是專門存放應答Native端內容的,每次應答之後都會置空然後等待下一次的會話到來。 那麼,問題來了!應答Native端的內容是如何被放進this._queue數組裡面的呢? 有點抽象,也有點玄乎了,我們不妨結合一下場景分析:假設用戶點擊了文本,然後手機彈出一個Toast,這個Toast其實就是來自Javascript的應答,如果用React-Native代碼寫出來應該是這樣: 當然,彈這個Toast效果是需要Native端來做了,但是‘Awesome, Clicking!’文案和SHORT,兩個參數是來自Javascript端的,Javascript端會告訴Native端彈一個內容‘Awesome, Clicking!’時長SHORT的Toast,其實就是對用戶點擊這個會話的應答了。 那我麼就以ToastAndroid為例,來分析下Javascript的應答,也就是如何把Awesome, Clicking!’和SHORT兩個參數塞進this._queue數組的。 先來研究一下ToastAndroid的代碼,位於\node_modules\react-native\Libraries\Components\ToastAndroid\ToastAndroid.android.js 裡面使用的是RCTToastAndroid,而RCTToastAndroid又是NativeModules裡的一個屬性。所以,這一段Toast的調用代碼等價於: 下面來看看NativeModules吧,代碼位於\node_modules\react-native\Libraries\BatchedBridge\BatchedBridgedModules\NativeModules.js 乍一看,NativeModules對象裡面是空的,並沒有所謂的RCTToastAndroid屬性,但是不要忘了,Javascript是可以通過Object.defineProperty方式定義對象屬性的,也算是其獨門絕技了,仔細閱讀一下NativeModules的代碼,果然找到了一些蛛絲馬跡,我們來看看: 這段代碼的意思是遍歷RemoteModules對象,將其屬性名定義成NativeModules的屬性名,而屬性值通過get方法返回,同時這裡還有一個if語句判斷,作用是如果當前moduleName對象未加載,將初始化一個並存入RemoteModules中供下一次調用。總結一下,這段代碼代碼的作用可以看成是NativeModules對RemoteModules對象的一次拷貝。 那麼,再次還原一下,Toast的調用變成了 下面,來看看RemoteModules對象裡面具體是由哪些內容。這裡的RemoteModules引用的是BatchedBridge.RemoteModules,也就是MessageQueue.RemoteModules RemoteModules是在MessageQueue的構造函數裡面通過_genModules方法初始化數據的。 前篇中我們研究過構造函數中localModules參數的來源,同樣的remoteModules參數也是一個來自Native端的JSON對象,裡面存放著所有NativeModule組件信息,格式如下圖所示: 先來看一下_genModulesConfig方法 這一段代碼是對remoteModules這個JSON格式對象的處理,生成一個以moduleID為鍵,module數組為值的集合moduleArray,module數組中按順序存放著: 這個moduleArray又被交給_genModules方法做進一步處理: 代碼比較長,但是不難,遍歷moduleArray集合,生成一個新的module對象,然後賦值給this.RemoteModules[moduleName],最終結果等價於: methods裡面每個methodName都通過_genMethod方法被賦值成一個function: 通過這個方法,this.RemoteModules再具體化一下,將出現 即 如果是調用Toast組件,moduleName = RCTToastAndroid,methodName =show,具體化一下,變成了 在剛剛分析NativeModules的時候,說過Toast調用的執行語句是 比較上面兩個Javascript語句,show方法說明實際調用的是__nativeCall,由此證明了一個觀點:所有NativeModules組件的調用,最終都是調用的MessageQueue .__nativeCall。 仔細想想,這樣一個過程,是不是和前篇所分析的JavascriptModules調用最終是在JavaScriptModuleInvocationHandler裡面統一調callFunction無比相似呢? 現在可以揭開this._queue數組內容之謎了!我們來看__nativeCall: onFail和onSucc兩個參數是用於Javascript端接收Native端回調的,如果不需要回調的話這兩個參數都是null,可以忽略(這個回調過程先挖個坑,將會是下一篇博客的內容^-^)。 由於global.nativeFlushQueueImmediate的值是undefined,所以if語句也不會走到,所以__nativeCall中真正有用的代碼就四行,再結合flushedQueue方法分析一下: _callID自增計數,在flushedQueue中向Native應答後清空this._queue數組時_callID會預先放置到其中,計數下一次會話的應答。 向this._queue數組中添加數據時,使用的是push,而不是賦值,說明了this._queue可以接受多個NativeModules的調用數據,然後在一次應答Native的通信中全部傳遞給Native端。 舉個例子,我點擊文本,可以同時執行多個RCTToastAndroid調用,但這個請求會一次性發給Native端,這樣可以提高通信效率。 再回到之前Toast的場景,執行完上面的Javascript語句後,this._queue數組中應該有以下內容了(假設moduleID=0,methodID=0),後面來分析Bridge層的調用。 2、Bridge層的轉發 this._queue數組被轉成JSON字符串,作為Javascript端的應答被返回給了Bridge層。 回到jni/react/JSCExecutor.cpp中的callFunction方法: 裡面執行的是jni/react/Bridge.cpp的callNativeModules: 再裡面又是調的m_callback的onCallNativeModules,同時又先將callJSON做了解析。先來看看callJSON被parseMethodCalls方法解析之後的樣子吧,代碼位於jni/react/MethodCall.cpp中 代碼和Javascript中this._queue數組存儲的時候非常相似,只不過這邊是讀取,然後封裝成一個MethodCall對象。 回到m_callback->onCallNativeModules那段代碼中,Bridge.cpp的m_callback對象是BridgeCallback的實例,在其構造函數中傳入。而Bridge是在OnLoad.cpp中實例化的。 這裡m_callback真正的引用是PlatformBridgeCallback,它是BridgeCallback的子類,所以來看PlatformBridgeCallback的onCallNativeModules方法。 onCallNativeModules方法調用的是executeCallbackOnCallbackQueueThread,字面意思是在回調隊列線程中執行回調,被執行的回調方法裡面對calls進行遍歷,然後分別執行makeJavaCall(前面提過Javascript會將多個執行結果放到一次應答通信中回調給Native)。 executeCallbackOnCallbackQueueThread方法裡面會創建一個Java類Runnable,然後將其塞入隊列中enqueueNativeRunnableOnQueue,當Runnable回調被執行時,也就是上面的makeJavaCall被遍歷執行了。 這個所謂的executeCallbackOnCallbackQueueThread由PlatformBridgeCallback的兩個構造參數weakCallback_和weakCallbackQueueThread_來執行的,這兩個參數對象又是由Java層構建,傳到create方法,又傳給PlatformBridgeCallback對象。 上面代碼可以看出: 再來看ReactCallback的回調裡面makeJavaCall方法裡面干了啥。 這一連串的jni調用結果就是,來自Javascript層的moduleId、methodId、args,被調用到Java層的ReactCallback的call方法裡面,Bridge層的流程也就到此結束了。 當然,onCallNativeModules方法裡面最後還有一個signalBatchComplete方法,也是ReactCallback.java的回調,意圖是告訴Native端,從Native->Javascript->Native一次完整的通信結束。 Bridge層的邏輯其實非常簡單,都是簡單的調用Java層的對象,下面就開始分析Java層的處理邏輯了,聰明些的同學可能已經猜測到具體流程了! 3、Java層的接收 人類向宇宙深處發射無線電波,經歷了無數個歲月終於接收到其他文明的回應了,Native與Javascript通信其實也是一個非常相似的過程,作為Native端的接收者ReactCallback,到底做了什麼呢? 首先,來瞧瞧Java層的ReactCallback對象是怎樣創建的,先來看下CatalystInstanceImpl中ReactBridge初始化的過程: ReactBridge構造方法裡面,調用initialize這個native方法,將創建的NativeModulesReactCallback對象傳到了JNI層,而NativeModulesReactCallback又是ReactCallback的直接子類,所以JNI層調用的ReactCallback其實就是NativeModulesReactCallback對象了。 NativeModulesReactCallback是CatalystInstanceImpl的一個內部類,實現了ReactCallback的兩個抽象方法:call和onBatchComplete。 onBatchComplete是用來通知Native->Javascript->Native的一次雙向通信完成的,通知到各個監聽器,處理一些特殊邏輯,比如視圖刷新之類,這個過程與本文主題無關,就暫不深入研究了。 而call是接收Javascript端應答的,我們來分析一下: mJavaRegistry指的是NativeModuleRegistry,字面意思就是Native組件注冊表,call方法參數除了CatalystInstanceImpl外,還有來自Javascript端的moduleId, methodId, parameters三個。 不出所料的話,需要在NativeModuleRegistry注冊表裡面,通過moduleId匹配到注冊的Native組件,再通過methodId匹配到組件的方法,然後執行parameters。 至於為什麼能夠匹配上,別忘了,無論JavascriptModule還是NativeModule,所有的moduleId和 methodId,都是通過Native端的ReactBridge的setGlobalVariable方法傳遞到Javascript端的,這個邏輯在前篇中已經重點研究過了! NativeModuleRegistry的call方法很簡單,在com.facebook.react.bridge包下: 和MessageQueue.js中保存著JavascriptModule的映射表一樣,NativeModuleRegistry 中也保持著NativeModule的映射表,名為mModuleTable。 NativeModule注冊的過程中,會生成代表自身且唯一的moduleID,同時其內部所有public方法也會生成唯一的methodID,這些信息都保存在一個名叫ModuleDefinition的對象中,最終走的也是它的call方法,而ModuleDefinition則是NativeModuleRegistry的一個內部類,代碼如下: ModuleDefinition的call方法裡面寫得有點不嚴謹,不過無關緊要,走的是MethodRegistration的method成員變量的invoke方法。 ModuleDefinition是NativeModule內方法信息的封裝類,代碼也在NativeModuleRegistry中: 其內部method成員變量是NativeModule.NativeMethod對象,真正的實現則是JavaMethod類,後者是BaseJavaModule的內部類。而BaseJavaModule是NativeModule的抽象實現,所以所有的Native組件類都是其直接或間接子類,比如我們常用的ToastModule就是它的一個間接子類! 接下來,我們來看JavaMethod的invoke方法: 這應該是最終的調用了,由於來自Javascript端的args參數,都在JNI層裡被封裝成ReadableNativeArray對象,比如例子中Toast的‘Awesome, Clicking!’文案和時長SHORT,一個是字符串String,一個是整型int,都被封裝在ReadableNativeArray裡,那麼這裡就需要進行提取了。 這裡定義了一個提取器,名為ArgumentExtractor,是個抽象類: 在BaseJavaModule內部定義了9種類型的提取器用於處理不同類型的參數,如下表: 最後兩個比較特殊,Callback和Promise類型,後面博文中我會一一分析,這裡順帶提一下。 還是Toast那個例子,顯示文案String類型和顯示時長int類型,都被提取出來了,存放到了mArguments數組中,現在萬事具備只剩東風了, mMethod指被調用的方法,用於反射的java.lang.reflect.Method對象,BaseJavaModule.this指代當前NativeModule對象的實例,如果是Toast組件的話就是ToastModule了,mArguments是參數。invoke反射NativeModule的目標方法,完成Java層的最終調用。 如果是彈Toast,被反射的就是ToastModule的show方法了: 參數message是Awesome, Clicking!’,duration是Toast.LENGTH_SHORT。 4、總結 Javascript端調用Native端,同樣分了三層:Javascript、Bridge、Java,概括一下主要流程。 A、Javascript層: B、Bridge層: C、Java層: 全部過程用一張流程圖大概描述如下: Javascript端向Native端通信到此結束,結合前篇,正常情況下Native與Javascript通信機制應該是完整了,但是但是,還缺少了一些東西。不妨想象一下這樣的場景:Javascript想要實時獲取App頁面的生命周期狀態,處在前台運行還是後台運行?這就需要Javascript->Native->Javascript這種機制了,也就是說在Native在接收到Javascript的應答後還應該給Javascript一個反饋,與前篇Native主動調用Javascript不同的是這是一個被動回調的過程。 謝謝閱讀,下篇再見! void JSCExecutor::callFunction(const std::string& moduleId, const std::string& methodId, const folly::dynamic& arguments) {
// TODO: Make this a first class function instead of evaling. #9317773
std::vector
static std::string executeJSCallWithJSC(
JSGlobalContextRef ctx,
const std::string& methodName,
const std::vector
MessageQueue.callFunctionReturnFlushedQueue.apply(null, module, method, args);
callFunctionReturnFlushedQueue(module, method, args) {
guard(() => {
this.__callFunction(module, method, args);
this.__callImmediates();
});
return this.flushedQueue();
}
flushedQueue() {
this.__callImmediates();
let queue = this._queue;
this._queue = [[], [], [], this._callID];
return queue[0].length ? queue : null;
}
var ToastAndroid = require('ToastAndroid')
ToastAndroid.show('Awesome, Clicking!', ToastAndroid.SHORT);
'use strict';
var RCTToastAndroid = require('NativeModules').ToastAndroid;
var ToastAndroid = {
SHORT: RCTToastAndroid.SHORT,
LONG: RCTToastAndroid.LONG,
show: function (
message: string,
duration: number
): void {
RCTToastAndroid.show(message, duration);
},
};
module.exports = ToastAndroid;
NativeModules.RCTToastAndroid.show(message, duration);
const BatchedBridge = require('BatchedBridge');
const RemoteModules = BatchedBridge.RemoteModules;
...
const NativeModules = {};
...
module.exports = NativeModules;
const NativeModules = {};
Object.keys(RemoteModules).forEach((moduleName) => {
Object.defineProperty(NativeModules, moduleName, {
enumerable: true,
get: () => {
let module = RemoteModules[moduleName];
if (module && typeof module.moduleID === 'number' && global.nativeRequireModuleConfig) {
const json = global.nativeRequireModuleConfig(moduleName);
const config = json && JSON.parse(json);
module = config && BatchedBridge.processModuleConfig(config, module.moduleID);
RemoteModules[moduleName] = module;
}
return module;
},
});
});
RemoteModules.RCTToastAndroid.show(message, duration);
class MessageQueue {
constructor(remoteModules, localModules) {
this.RemoteModules = {};
...
let modulesConfig = this._genModulesConfig(remoteModules);
this._genModules(modulesConfig);
...
}
_genModulesConfig(modules /* array or object */) {
if (Array.isArray(modules)) {
return modules;
} else {
let moduleArray = [];
let moduleNames = Object.keys(modules);
for (var i = 0, l = moduleNames.length; i < l; i++) {
let moduleName = moduleNames[i];
let moduleConfig = modules[moduleName];
let module = [moduleName];
if (moduleConfig.constants) {
module.push(moduleConfig.constants);
}
let methodsConfig = moduleConfig.methods;
if (methodsConfig) {
let methods = [];
let asyncMethods = [];
let methodNames = Object.keys(methodsConfig);
for (var j = 0, ll = methodNames.length; j < ll; j++) {
let methodName = methodNames[j];
let methodConfig = methodsConfig[methodName];
methods[methodConfig.methodID] = methodName;
if (methodConfig.type === MethodTypes.remoteAsync) {
asyncMethods.push(methodConfig.methodID);
}
}
if (methods.length) {
module.push(methods);
if (asyncMethods.length) {
module.push(asyncMethods);
}
}
}
moduleArray[moduleConfig.moduleID] = module;
}
return moduleArray;
}
}
[moduleName,constants,methods,asyncMethods]
_genModules(remoteModules) {
remoteModules.forEach((config, moduleID) => {
this._genModule(config, moduleID);
});
}
_genModule(config, moduleID) {
if (!config) {
return;
}
let moduleName, constants, methods, asyncMethods;
if (moduleHasConstants(config)) {
[moduleName, constants, methods, asyncMethods] = config;
} else {
[moduleName, methods, asyncMethods] = config;
}
let module = {};
methods && methods.forEach((methodName, methodID) => {
const methodType =
asyncMethods && arrayContains(asyncMethods, methodID) ?
MethodTypes.remoteAsync : MethodTypes.remote;
module[methodName] = this._genMethod(moduleID, methodID, methodType);
});
Object.assign(module, constants);
if (!constants && !methods && !asyncMethods) {
module.moduleID = moduleID;
}
this.RemoteModules[moduleName] = module;
return module;
}
methods.forEach((methodName, methodID){
methods[methodName] = this._genMethod(moduleID, methodID, methodType);
}
this.RemoteModules[moduleName] = {moduleID, constants, methods};
_genMethod(module, method, type) {
let fn = null;
let self = this;
if (type === MethodTypes.remoteAsync) {
fn = function(...args) {
return new Promise((resolve, reject) => {
self.__nativeCall(...);
});
};
} else {
fn = function(...args) {
...
return self.__nativeCall(...);
};
}
fn.type = type;
return fn;
}
this.RemoteModules[moduleName][methodName] = __nativeCall(...);
this.RemoteModules.moduleName.methodName = __nativeCall(...);
this.RemoteModules.RCTToastAndroid.show = __nativeCall(...);
RemoteModules.RCTToastAndroid.show(message, duration);
__nativeCall(module, method, params, onFail, onSucc) {
...
this._callID++;
this._queue[MODULE_IDS].push(module);
this._queue[METHOD_IDS].push(method);
this._queue[PARAMS].push(params);
var now = new Date().getTime();
if (global.nativeFlushQueueImmediate &&
now - this._lastFlush >= MIN_TIME_BETWEEN_FLUSHES_MS) {
global.nativeFlushQueueImmediate(this._queue);
this._queue = [[], [], [], this._callID];
this._lastFlush = now;
}
...
}
__nativeCall(module, method, params, onFail, onSucc) {
...
this._callID++;
this._queue[MODULE_IDS].push(module);
this._queue[METHOD_IDS].push(method);
this._queue[PARAMS].push(params);
...
}
flushedQueue() {
...
let queue = this._queue;
this._queue = [[], [], [], this._callID];
return queue[0].length ? queue : null;
}
var ToastAndroid = require('ToastAndroid')
ToastAndroid.show('Awesome, Clicking!', ToastAndroid.SHORT);
this._queue = [[0], [0], ['Awesome, Clicking!','SHORT'], this._callID];
void JSCExecutor::callFunction(...) {
...
std::string calls = executeJSCallWithJSC(...);
m_bridge->callNativeModules(*this, calls, true);
}
void Bridge::callNativeModules(JSExecutor& executor, const std::string& callJSON, bool isEndOfBatch) {
if (*m_destroyed) {
return;
}
m_callback->onCallNativeModules(getTokenForExecutor(executor), parseMethodCalls(callJSON), isEndOfBatch);
}
#define REQUEST_MODULE_IDS 0
#define REQUEST_METHOD_IDS 1
#define REQUEST_PARAMSS 2
#define REQUEST_CALLID 3
std::vector
static void create(JNIEnv* env, jobject obj, jobject executor, jobject callback, jobject callbackQueueThread) {
auto weakCallback = createNew
class PlatformBridgeCallback : public BridgeCallback {
public:
PlatformBridgeCallback(
RefPtr
registerNatives("com/facebook/react/bridge/ReactBridge", {
makeNativeMethod("initialize", "(Lcom/facebook/react/bridge/JavaScriptExecutor;Lcom/facebook/react/bridge/ReactCallback;Lcom/facebook/react/bridge/queue/MessageQueueThread;)V", bridge::create),
...
});
weakCallback_參數是com.facebook.react.bridge.ReactCallback的實例;
weakCallbackQueueThread_參數是com.facebook.react.bridge.queue.MessageQueueThread的實例;
兩者都是通過com.facebook.react.bridge.ReactBridge通過initialize本地方法傳入的。
static void makeJavaCall(JNIEnv* env, ExecutorToken executorToken, jobject callback, const MethodCall& call) {
if (call.arguments.isNull()) {
return;
}
...
auto newArray = ReadableNativeArray::newObjectCxxArgs(std::move(call.arguments));
env->CallVoidMethod(
callback,
gCallbackMethod,
static_cast
jclass callbackClass = env->FindClass("com/facebook/react/bridge/ReactCallback");
bridge::gCallbackMethod = env->GetMethodID(callbackClass, "call", "(Lcom/facebook/react/bridge/ExecutorToken;IILcom/facebook/react/bridge/ReadableNativeArray;)V");
static void signalBatchComplete(JNIEnv* env, jobject callback) {
env->CallVoidMethod(callback, gOnBatchCompleteMethod);
}
jclass callbackClass = env->FindClass("com/facebook/react/bridge/ReactCallback");
bridge::gOnBatchCompleteMethod = env->GetMethodID(callbackClass, "onBatchComplete", "()V");
public class CatalystInstanceImpl implements CatalystInstance {
...
private ReactBridge initializeBridge(JavaScriptExecutor jsExecutor, JavaScriptModulesConfig jsModulesConfig) {
...
ReactBridge bridge;
try {
bridge = new ReactBridge(jsExecutor, new NativeModulesReactCallback(), mReactQueueConfiguration.getNativeModulesQueueThread());
} finally {
...
}
...
return bridge;
}
...
}
public class ReactBridge extends Countable {
...
public ReactBridge(JavaScriptExecutor jsExecutor, ReactCallback callback, MessageQueueThread nativeModulesQueueThread) {
mJSExecutor = jsExecutor;
mCallback = callback;
mNativeModulesQueueThread = nativeModulesQueueThread;
initialize(jsExecutor, callback, mNativeModulesQueueThread);
}
private native void initialize(JavaScriptExecutor jsExecutor, ReactCallback callback, MessageQueueThread nativeModulesQueueThread);
...
}
jclass callbackClass = env->FindClass("com/facebook/react/bridge/ReactCallback");
bridge::gCallbackMethod = env->GetMethodID(callbackClass, "call", "(Lcom/facebook/react/bridge/ExecutorToken;IILcom/facebook/react/bridge/ReadableNativeArray;)V");
private class NativeModulesReactCallback implements ReactCallback {
@Override
public void call(int moduleId, int methodId, ReadableNativeArray parameters) {
...
mJavaRegistry.call(CatalystInstanceImpl.this, moduleId, methodId, parameters);
}
@Override
public void onBatchComplete() {
...
}
}
public class NativeModuleRegistry {
...
private final List
private static class ModuleDefinition {
public final int id;
public final String name;
public final NativeModule target;
public final ArrayList
private static class MethodRegistration {
public MethodRegistration(String name, String tracingName, NativeModule.NativeMethod method) {
this.name = name;
this.tracingName = tracingName;
this.method = method;
}
public String name;
public String tracingName;
public NativeModule.NativeMethod method;
}
private class JavaMethod implements NativeMethod {
...
private Method mMethod;
private final ArgumentExtractor[] mArgumentExtractors;
private final Object[] mArguments;
@Override
public void invoke(CatalystInstance catalystInstance, ReadableNativeArray parameters){
...
int i = 0, jsArgumentsConsumed = 0;
try {
for (; i < mArgumentExtractors.length; i++) {
mArguments[i] = mArgumentExtractors[i].extractArgument(catalystInstance, parameters, jsArgumentsConsumed);
jsArgumentsConsumed +=mArgumentExtractors[i].getJSArgumentsNeeded();
}
} catch (UnexpectedNativeTypeException e) {
throw new NativeArgumentsParseException(
...
}
mMethod.invoke(BaseJavaModule.this, mArguments);
...
}
...
}
private static abstract class ArgumentExtractor
ARGUMENT_EXTRACTOR_BOOLEAN
ARGUMENT_EXTRACTOR_DOUBLE
ARGUMENT_EXTRACTOR_FLOAT
ARGUMENT_EXTRACTOR_INTEGER
ARGUMENT_EXTRACTOR_STRING
ARGUMENT_EXTRACTOR_ARRAY
ARGUMENT_EXTRACTOR_MAP
ARGUMENT_EXTRACTOR_CALLBACK
ARGUMENT_EXTRACTOR_PROMISE
mMethod.invoke(BaseJavaModule.this, mArguments);
public class ToastModule extends ReactContextBaseJavaModule {
private static final String DURATION_SHORT_KEY = "SHORT";
private static final String DURATION_LONG_KEY = "LONG";
public ToastModule(ReactApplicationContext reactContext) {
super(reactContext);
}
@Override
public String getName() {
return "ToastAndroid";
}
...
@ReactMethod
public void show(String message, int duration) {
Toast.makeText(getReactApplicationContext(), message, duration).show();
}
}
邏輯最為復雜的一層,NativeModules.js組件是向Native端調用的入口,其指向的又是MessageQueue的RemoteModules對象,而RemoteModules將所有的method指向了一個function函數,即__nativeCall。__nativeCall負責將所有向Native端請求的信息push進this._queue數組,在應答Native端通信請求的flushedQueue方法內將this._queue返給Bridge。
當Bridge層接收到Javascript層的應答信息this._queue(一個JSON字符串)後,調用PlatformBridgeCallback對象的onCallNativeModules方法,onCallNativeModules裡面創建了一個Runnable塞到執行Callback的線程隊列中等待回調,回調中執行makeJavaCall方法,裡面最終通過env->CallVoidMethod調用了Java層的方法。
Bridge層中調起了Java層NativeModulesReactCallback的call方法,其裡面又是NativeModuleRegistry的call調用。NativeModuleRegistry通過moduleID從保存在其內部的NativeModule映射表,匹配到需要被執行的NativeModule對象,再通過methodID匹配到NativeModule的方法。最後從ReadableNativeArray中提取出參數後通過invoke反射方式執行NativeModule的方法。
Android開發通用的工具類在開發中有些代碼都是重復性的,如果能把這些代碼集中的分類提取出來(比如網絡連接、數據保存等),然後再以後寫帶碼中,直接把這些代碼復制過來,然
深入理解Intent和IntentFiler(一) Jiangdg_VIP http://blog.csdn.net/u012637501 為了比較深刻
一、先在中文官網官網地址找到教案。二、Homebrew裝上(mac版本直接輸入下面的命令)。/usr/bin/ruby -e "$(curl -fsSL htt
不廢話,先上圖,看看效果 這是用ExpandableListView來實現時間軸效果,原理比較簡單,以月份為第一級,以天為第二級來實現的。