React Native通信機(jī)制詳解

最近在搗鼓跨平臺(tái)開(kāi)發(fā),最終選擇使用React Native.這對(duì)于不太了解前端的原生開(kāi)發(fā)者來(lái)說(shuō),坑還是挺多的.
今天我們來(lái)了解下RN的通信機(jī)制

react-native用iOS自帶的JavaScriptCore作為JS的解析引擎,但并沒(méi)有用到JavaScriptCore提供的一些可以讓JS與OC互調(diào)的特性,而是實(shí)現(xiàn)了一套機(jī)制,這套機(jī)制可以通用在所有的JS引擎上,在沒(méi)有JavaScriptCore的情況下,也可以用webview代替,實(shí)際上項(xiàng)目匯總已經(jīng)有用webview作為引擎的實(shí)現(xiàn),應(yīng)該是用于兼容ios7以下沒(méi)有JavaScriptCore的版本

普通的JS-OC通信很簡(jiǎn)單,OC向JS傳信息有現(xiàn)成的接口,比如webview提供的stringByEvaluatingJavaScriptFromString方法可以直接在當(dāng)前的context上執(zhí)行一段JS腳本,并且可以獲取執(zhí)行后的返回值,這個(gè)返回值就相當(dāng)于JS向OC傳遞信息.react-native也是以此為基礎(chǔ),通過(guò)各種手段,實(shí)現(xiàn)了在OC定義一個(gè)模塊方法,JS可以直接調(diào)用這個(gè)模塊方法并還可以無(wú)縫銜接回調(diào)

模塊配置表

首先OC要 告訴JS她有什么模塊,模塊里面有什么方法,JS才知道有這些方法后才有可能去調(diào)用這些方法,這里的實(shí)現(xiàn)是OC生成一分模塊配置表傳給JS,配置表里包括了所有模塊和模塊里方法的信息,例如:


01.png

OC端和JS端分別有一個(gè)bridge,兩個(gè)bridge都保存了同樣一份模塊配置表,JS調(diào)用OC模塊方法時(shí),通過(guò)bridge里的配置表把模塊方法轉(zhuǎn)為模塊ID和方法ID傳給OC,OC通過(guò)bridge的模塊配置表找到對(duì)應(yīng)的方法執(zhí)行之,以上述代碼為例,流程大致是這樣:


02.png

在了解這個(gè)調(diào)用流程之前,我們先來(lái)看看OC的模塊配置表是怎么來(lái)的,我們?cè)谛陆ㄒ粋€(gè)OC模塊時(shí),JS和OC都不需要為新的模塊去手動(dòng)添加一些配置,模塊配置是自動(dòng)生成的,只要項(xiàng)目中有一個(gè)模塊,就會(huì)把這個(gè)模塊添加到配置表里,那這個(gè)模塊配置表示怎樣生成的呢?分以下兩個(gè)步驟:
1,取所有模塊類(lèi)

每個(gè)模塊類(lèi)都實(shí)現(xiàn)了RCTBridgeModule接口,可以通過(guò)runtime接口objc_getClassList或者objc_copyClassList取出項(xiàng)目里所有類(lèi),然后逐個(gè)判斷是否實(shí)現(xiàn)了RCTBridgeModule接口,就可以找到所有模塊類(lèi),實(shí)現(xiàn)在RCTBridgeModuleClassByModule()方法里.
2,取出模塊里暴露給JS的方法

一個(gè)模塊里可以有很多方法,一些是可以暴露給JS直接調(diào)用的,一些是私有的不想暴露給JS的,怎樣做到提取這些暴露的方法呢?我能想到的方法是對(duì)要暴露的方法名制定一些規(guī)則,比如用RCTExport作為前綴,然后用runtime方法class_getInstaceMethod取出所有方法名字,提取以RCTExport為前綴的方法,但是這樣做惡心的地方就是每個(gè)方法必須加前綴,react-native用了另一個(gè)黑魔法似的方法解決這個(gè)問(wèn)題:編譯屬性attribute;
在上述例子中我們看到模塊方法里有句代碼:RCT_EXPORT(),模塊里的方法加上這個(gè)宏就可以實(shí)現(xiàn)暴露給JS,無(wú)需其他規(guī)則,那這個(gè)宏做了什么呢?來(lái)看看他的定義:

4.png

這個(gè)宏的作用是用編譯屬性attribute給二進(jìn)制文件新建一個(gè)section,屬于__DATA數(shù)據(jù)段,名字為RCTExport,并在這個(gè)段里加入當(dāng)前方法名。編譯器在編譯時(shí)會(huì)找到attribute進(jìn)行處理,為生成的可執(zhí)行文件加入相應(yīng)的內(nèi)容。效果可以從linkmap看出來(lái):
5.png

可以看到可執(zhí)行文件數(shù)據(jù)段多了個(gè)RCTExport段,內(nèi)容就是各個(gè)要暴露給JS的方法,這些內(nèi)容是可以在運(yùn)行時(shí)獲取到的,在RCTBridge的RCTExportMethodByModuleID()方法里獲取這些內(nèi)容,提取每個(gè)方法的類(lèi)名和方法名,就完成了提取模塊里暴露給JS方法的工作.
整個(gè)模塊類(lèi)/方法提取實(shí)現(xiàn)在RCTRemoteModulesConfig()方法里

調(diào)用流程

接下來(lái)看看JS調(diào)用OC模塊方法的詳細(xì)流程,包括callBack回調(diào),這時(shí)需要細(xì)化一下上述的調(diào)用流程圖:


03.png

看起來(lái)有點(diǎn)復(fù)雜,從發(fā)起調(diào)用到執(zhí)行回調(diào)總共11個(gè)步驟,下面來(lái)說(shuō)明下:
1,JS端調(diào)用某個(gè)OC模塊暴露出來(lái)的方法
2,把上一步的調(diào)用分解為ModuleName,MethodName,arguments,扔給MessageQueue處理.在初始化時(shí)模塊配置表上的每一個(gè)模塊都生成了對(duì)應(yīng)的remoteModule對(duì)象,對(duì)象里也生成了跟模塊配置表里一一對(duì)應(yīng)的方法,這些方法里可以拿到自身的模塊名,方法名,并對(duì)callBack進(jìn)行一些處理,再移交給MessageQueue,具體實(shí)現(xiàn)在BatchedBridgeFactory.js的_createBridgeModule里.整個(gè)實(shí)現(xiàn)24行代碼.
3,在這一步把JS的callback函數(shù)緩存在MessageQueue的一個(gè)成員變量里,用CallbackID代表callback.再通過(guò)保存在MessageQueue的模塊配置表把上一步傳進(jìn)來(lái)的ModuleName和MethodName轉(zhuǎn)為ModuleID和MethodID;
4,把上述步驟得到的ModuleID,MethodID,callbackID和其他參數(shù)argus傳給OC.
5,OC接受到消息,通過(guò)模塊配置表拿到對(duì)應(yīng)的模塊和方法
6,RCTModuleMethod對(duì)JS傳過(guò)來(lái)的每一個(gè)參數(shù)進(jìn)行處理
7,OC模塊方法調(diào)用完畢,執(zhí)行block回調(diào)
8,調(diào)用到第六步說(shuō)明的RCTModuleMethod生成的block
9,block里帶著CallbackID和block傳過(guò)來(lái)的參數(shù)去調(diào)JS里MessageQueue的方法nvokeCallBackAndReturnFlushQueue
10,MessageQueue通過(guò)callbackID找到對(duì)應(yīng)的JScallback方法
11,調(diào)用callback方法,并把OC帶過(guò)來(lái)的參數(shù)一起傳遞過(guò)去,完成回調(diào)
整個(gè)流程概括為:
JS函數(shù)調(diào)用轉(zhuǎn)ModuleID/MethodID—>callback轉(zhuǎn)CallbackID—>OC根據(jù)ID拿到方法—>處理參數(shù)—>調(diào)用OC方法—>回調(diào)callbackID—>JS通過(guò)callbackID拿到callback執(zhí)行

事件響應(yīng)

上述第四步有一個(gè)問(wèn)題:JS是怎樣把數(shù)據(jù)傳給OC,讓OC去調(diào)用相應(yīng)方法?
答案是通過(guò)返回值:JS不會(huì)主動(dòng)傳遞數(shù)據(jù)給OC,在調(diào)用OC方法時(shí),會(huì)在上述第四步把ModuleID,MethodID等數(shù)據(jù)加到一個(gè)隊(duì)列里,等OC過(guò)來(lái)調(diào)JS的任意方法時(shí),再把這個(gè)隊(duì)列放回給OC,此時(shí)OC在執(zhí)行這個(gè)隊(duì)列里要調(diào)用的方法

總結(jié)

整個(gè)React Native的JS-OC通信機(jī)制大致就是這樣了,關(guān)鍵點(diǎn)在于:模塊化,模塊配置表,傳遞ID,封裝調(diào)用,事件響應(yīng),其設(shè)計(jì)思想和實(shí)現(xiàn)方法很值得學(xué)習(xí)借鑒。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,517評(píng)論 6 539
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,087評(píng)論 3 423
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 177,521評(píng)論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,493評(píng)論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,207評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,603評(píng)論 1 325
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,624評(píng)論 3 444
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,813評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,364評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,110評(píng)論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,305評(píng)論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,874評(píng)論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,532評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,953評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 36,209評(píng)論 1 291
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,033評(píng)論 3 396
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,268評(píng)論 2 375

推薦閱讀更多精彩內(nèi)容