日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

React Native通信机制详解

發(fā)布時(shí)間:2023/12/10 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 React Native通信机制详解 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

http://blog.cnbang.net/tech/2698/

React Native是facebook剛開(kāi)源的框架,可以用javascript直接開(kāi)發(fā)原生APP,先不說(shuō)這個(gè)框架后續(xù)是否能得到大眾認(rèn)可,單從源碼來(lái)說(shuō),這個(gè)框架源碼里有非常多的設(shè)計(jì)思想和實(shí)現(xiàn)方式值得學(xué)習(xí),本篇先來(lái)看看它最基礎(chǔ)的JavaScript-ObjectC通信機(jī)制(以下簡(jiǎn)稱JS/OC)。

概覽

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通信實(shí)際上很簡(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)。

舉個(gè)例子,OC定義了一個(gè)模塊RCTSQLManager,里面有個(gè)方法-query:successCallback:,JS可以直接調(diào)用RCTSQLManager.query并通過(guò)回調(diào)獲取執(zhí)行結(jié)果。:

1 2 3 4 5 6 7 8 9 //OC: @implement RCTSQLManager - (void)query:(NSString *)queryData successCallback:(RCTResponseSenderBlOCk)responseSender { ?????RCT_EXPORT(); ?????NSString *ret = @"ret" ?????responseSender(ret); } @end
1 2 3 4 //JS: RCTSQLManager.query("SELECT * FROM table", function(result) { ?????//result == "ret"; });

接下來(lái)看看它是怎樣實(shí)現(xiàn)的。

模塊配置表

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

01 02 03 04 05 06 07 08 09 10 11 12 13 14 { ????"remoteModuleConfig": { ????????"RCTSQLManager": { ????????????"methods": { ????????????????"query": { ????????????????????"type": "remote", ????????????????????"methodID": 0 ????????????????} ????????????}, ????????????"moduleID": 4 ????????}, ????????... ?????}, }

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í)行之,以上述代碼為例,流程大概是這樣(先不考慮callback):

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

1.取所有模塊類

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

2.取模塊里暴露給JS的方法

一個(gè)模塊里可以有很多方法,一些是可以暴露給JS直接調(diào)用的,一些是私有的不想暴露給JS,怎樣做到提取這些暴露的方法呢?我能想到的方法是對(duì)要暴露的方法名制定一些規(guī)則,比如用RCTExport_作為前綴,然后用runtime方法class_getInstanceMethod取出所有方法名字,提取以RCTExport_為前綴的方法,但這樣做惡心的地方是每個(gè)方法必須加前綴。React Native用了另一種黑魔法似的方法解決這個(gè)問(wèn)題:編譯屬性__attribute__。

在上述例子中我們看到模塊方法里有句代碼:RCT_EXPORT(),模塊里的方法加上這個(gè)宏就可以實(shí)現(xiàn)暴露給JS,無(wú)需其他規(guī)則,那這個(gè)宏做了什么呢?來(lái)看看它的定義:

1 2 #define RCT_EXPORT(JS_name) __attribute__((used, section("__DATA,RCTExport" \ ))) static const char *__rct_export_entry__[] = { __func__, #JS_name }

這個(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):

01 02 03 04 05 06 07 08 09 10 11 12 13 14 # Sections: # Address Size Segment Section 0x100001670 0x000C0180 __TEXT __text ... 0x10011EFA0 0x00000330 __DATA RCTExport 0x10011F2D0 0x00000010 __DATA __common 0x10011F2E0 0x000003B8 __DATA __bss ... 0x10011EFA0 0x00000010 [ 4] -[RCTStatusBarManager setStyle:animated:].__rct_export_entry__ 0x10011EFB0 0x00000010 [ 4] -[RCTStatusBarManager setHidden:withAnimation:].__rct_export_entry__ 0x10011EFC0 0x00000010 [ 5] -[RCTSourceCode getScriptText:failureCallback:].__rct_export_entry__ 0x10011EFD0 0x00000010 [ 7] -[RCTAlertManager alertWithArgs:callback:].__rct_export_entry__ ...

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

整體的模塊類/方法提取實(shí)現(xiàn)在RCTRemoteModulesConfig()方法里。

調(diào)用流程

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

看起來(lái)有點(diǎn)復(fù)雜,不過(guò)一步步說(shuō)明,應(yīng)該很容易弄清楚整個(gè)流程,圖中每個(gè)流程都標(biāo)了序號(hào),從發(fā)起調(diào)用到執(zhí)行回調(diào)總共有11個(gè)步驟,詳細(xì)說(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的_createBridgedModule里,整個(gè)實(shí)現(xiàn)區(qū)區(qū)24行代碼,感受下JS的魔力吧。

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。至于具體是怎么傳的,后面再說(shuō)。

5.OC接收到消息,通過(guò)模塊配置表拿到對(duì)應(yīng)的模塊和方法。

實(shí)際上模塊配置表已經(jīng)經(jīng)過(guò)處理了,跟JS一樣,在初始化時(shí)OC也對(duì)模塊配置表上的每一個(gè)模塊生成了對(duì)應(yīng)的實(shí)例并緩存起來(lái),模塊上的每一個(gè)方法也都生成了對(duì)應(yīng)的RCTModuleMethod對(duì)象,這里通過(guò)ModuleID和MethodID取到對(duì)應(yīng)的Module實(shí)例和RCTModuleMethod實(shí)例進(jìn)行調(diào)用。具體實(shí)現(xiàn)在_handleRequestNumber:moduleID:methodID:params:。

6.RCTModuleMethod對(duì)JS傳過(guò)來(lái)的每一個(gè)參數(shù)進(jìn)行處理。

RCTModuleMethod可以拿到OC要調(diào)用的目標(biāo)方法的每個(gè)參數(shù)類型,處理JS類型到目標(biāo)類型的轉(zhuǎn)換,所有JS傳過(guò)來(lái)的數(shù)字都是NSNumber,這里會(huì)轉(zhuǎn)成對(duì)應(yīng)的int/long/double等類型,更重要的是會(huì)為block類型參數(shù)的生成一個(gè)block。

例如-(void)select:(int)index response:(RCTResponseSenderBlock)callback 這個(gè)方法,拿到兩個(gè)參數(shù)的類型為int,block,JS傳過(guò)來(lái)的兩個(gè)參數(shù)類型是NSNumber,NSString(CallbackID),這時(shí)會(huì)把NSNumber轉(zhuǎn)為int,NSString(CallbackID)轉(zhuǎn)為一個(gè)block,block的內(nèi)容是把回調(diào)的值和CallbackID傳回給JS。

這些參數(shù)組裝完畢后,通過(guò)NSInvocation動(dòng)態(tài)調(diào)用相應(yīng)的OC模塊方法。

7.OC模塊方法調(diào)用完,執(zhí)行block回調(diào)。

8.調(diào)用到第6步說(shuō)明的RCTModuleMethod生成的block。

9.block里帶著CallbackID和block傳過(guò)來(lái)的參數(shù)去調(diào)JS里MessageQueue的方法invokeCallbackAndReturnFlushedQueue。

10.MessageQueue通過(guò)CallbackID找到相應(yīng)的JS callback方法。

11.調(diào)用callback方法,并把OC帶過(guò)來(lái)的參數(shù)一起傳過(guò)去,完成回調(diào)。

整個(gè)流程就是這樣,簡(jiǎn)單概括下,差不多就是: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)

上述第4步留下一個(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ì)在上述第4步把ModuleID,MethodID等數(shù)據(jù)加到一個(gè)隊(duì)列里,等OC過(guò)來(lái)調(diào)JS的任意方法時(shí),再把這個(gè)隊(duì)列返回給OC,此時(shí)OC再執(zhí)行這個(gè)隊(duì)列里要調(diào)用的方法。

一開(kāi)始不明白,設(shè)計(jì)成JS無(wú)法直接調(diào)用OC,需要在OC去調(diào)JS時(shí)才通過(guò)返回值觸發(fā)調(diào)用,整個(gè)程序還能跑得通嗎。后來(lái)想想純native開(kāi)發(fā)里的事件響應(yīng)機(jī)制,就有點(diǎn)理解了。native開(kāi)發(fā)里,什么時(shí)候會(huì)執(zhí)行代碼?只在有事件觸發(fā)的時(shí)候,這個(gè)事件可以是啟動(dòng)事件,觸摸事件,timer事件,系統(tǒng)事件,回調(diào)事件。而在React Native里,這些事件發(fā)生時(shí)OC都會(huì)調(diào)用JS相應(yīng)的模塊方法去處理,處理完這些事件后再執(zhí)行JS想讓OC執(zhí)行的方法,而沒(méi)有事件發(fā)生的時(shí)候,是不會(huì)執(zhí)行任何代碼的,這跟native開(kāi)發(fā)里事件響應(yīng)機(jī)制是一致的。

說(shuō)到OC調(diào)用JS,再補(bǔ)充一下,實(shí)際上模塊配置表除了有上述OC的模塊remoteModules外,還保存了JS模塊localModules,OC調(diào)JS某些模塊的方法時(shí),也是通過(guò)傳遞ModuleID和MethodID去調(diào)用的,都會(huì)走到-enqueueJSCall:args:方法把兩個(gè)ID和參數(shù)傳給JS的BatchedBridge.callFunctionReturnFlushedQueue,跟JS調(diào)OC原理差不多,就不再贅述了。

總結(jié)

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

總結(jié)

以上是生活随笔為你收集整理的React Native通信机制详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。