1. 程式人生 > >React Native和原生app通訊機制詳解

React Native和原生app通訊機制詳解

概述

React Native用iOS自帶的JavaScriptCore作為JS的解析引擎,但並沒有用到JavaScriptCore提供的一些可以讓JS與OC互調的特性,而是自己實現了一套機制,這套機制可以通用於所有JS引擎上,在沒有JavaScriptCore的情況下也可以用webview代替,實際上專案裡就已經有了用webview作為解析引擎的實現,應該是用於相容iOS7以下沒有JavascriptCore的版本。
普通的JS-OC通訊實際上很簡單,OC向JS傳資訊有現成的介面,像webview提供的-stringByEvaluatingJavaScriptFromString方法可以直接在當前context上執行一段JS指令碼,並且可以獲取執行後的返回值,這個返回值就相當於JS向OC傳遞資訊。React Native也是以此為基礎,通過各種手段,實現了在OC定義一個模組方法,JS可以直接呼叫這個模組方法並還可以無縫銜接回調。
舉個例子,OC定義了一個模組RCTSQLManager,裡面有個方法-query:successCallback:,JS可以直接呼叫RCTSQLManager.query並通過回撥獲取執行結果。

//OC
@implement RCTSQLManager
- (void)query:(NSString *)queryData successCallback:(RCTResponseSenderBlOCk)responseSender
{
     RCT_EXPORT();
     NSString *ret = @"ret"
     responseSender(ret);
}
@end
//JS:
RCTSQLManager.query("SELECT * FROM table", function(result) {
     //result == "ret";
});

模組配置表

首先OC要告訴JS它有什麼模組,模組裡有什麼方法,JS才知道有這些方法後才有可能去呼叫這些方法。這裡的實現是OC生成一份模組配置表傳給JS,配置表裡包括了所有模組和模組裡方法的資訊。例:

{
    "remoteModuleConfig": {
        "RCTSQLManager": {
            "methods": {
                "query": {
                    "type": "remote",
                    "methodID": 0
                }
            },
            "moduleID"
: 4 }, ... }, }

OC端和JS端分別各有一個bridge,兩個bridge都儲存了同樣一份模組配置表,JS呼叫OC模組方法時,通過bridge裡的配置表把模組方法轉為模組ID和方法ID傳給OC,OC通過bridge的模組配置表找到對應的方法執行之,以上述程式碼為例,流程大概是這樣(先不考慮callback):
這裡寫圖片描述
在瞭解這個呼叫流程之前,我們先來看看OC的模組配置表式怎麼來的。我們在新建一個OC模組時,JS和OC都不需要為新的模組手動去某個地方新增一些配置,模組配置表是自動生成的,只要專案裡有一個模組,就會把這個模組加到配置表上,那這個模組配置表是怎樣自動生成的呢?分兩個步驟。

1,取所有模組類

每個模組類都實現了RCTBridgeModule介面,可以通過runtime介面objc_getClassList或objc_copyClassList取出專案裡所有類,然後逐個判斷是否實現了RCTBridgeModule介面,就可以找到所有模組類,實現在RCTBridgeModuleClassesByModuleID()方法裡。

2,取模組裡暴露給JS的方法

一個模組裡可以有很多方法,一些是可以暴露給JS直接呼叫的,一些是私有的不想暴露給JS,怎樣做到提取這些暴露的方法呢?我能想到的方法是對要暴露的方法名制定一些規則,比如用RCTExport_作為字首,然後用runtime方法class_getInstanceMethod取出所有方法名字,提取以RCTExport_為字首的方法,但這樣做噁心的地方是每個方法必須加字首。React Native用了另一種黑魔法似的方法解決這個問題:編譯屬性attribute
在上述例子中我們看到模組方法裡有句程式碼:RCT_EXPORT(),模組裡的方法加上這個巨集就可以實現暴露給JS,無需其他規則,那這個巨集做了什麼呢?來看看它的定義:

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

這個巨集的作用是用編譯屬性attribute給二進位制檔案新建一個section,屬於DATA資料段,名字為RCTExport,並在這個段里加入當前方法名。編譯器在編譯時會找到__attribute進行處理,為生成的可執行檔案加入相應的內容。效果可以從linkmap看出來:

# 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__
...

可以看到可執行檔案資料段多了個RCTExport段,內容就是各個要暴露給JS的方法。這些內容是可以在執行時獲取到的,在RCTBridge.m的RCTExportedMethodsByModuleID()方法裡獲取這些內容,提取每個方法的類名和方法名,就完成了提取模組裡暴露給JS方法的工作。整體的模組類/方法提取實現在RCTRemoteModulesConfig()方法裡。

js和oc的呼叫流程分析

接下來看看JS呼叫OC模組方法的詳細流程,包括callback回撥。
這裡寫圖片描述
從發起呼叫到執行回撥總共有11個步驟:

  1. JS端呼叫某個OC模組暴露出來的方法。
  2. 把上一步的呼叫分解為ModuleName,MethodName,arguments,再扔給MessageQueue處理。
    在初始化時模組配置表上的每一個模組都生成了對應的remoteModule物件,物件裡也生成了跟模組配置表裡一一對應的方法,這些方法裡可以拿到自身的模組名,方法名,並對callback進行一些處理,再移交給MessageQueue。具體實現在BatchedBridgeFactory.js的_createBridgedModule裡。
  3. 在這一步把JS的callback函式快取在MessageQueue的一個成員變數裡,用CallbackID代表callback。在通過儲存在MessageQueue的模組配置表把上一步傳進來的ModuleName和MethodName轉為ModuleID和MethodID。
  4. 上述步驟得到的ModuleID,MethodId,CallbackID和其他引數argus傳給OC。
  5. OC接收到訊息,通過模組配置表拿到對應的模組和方法。實際上模組配置表已經經過處理了,跟JS一樣,在初始化時OC也對模組配置表上的每一個模組生成了對應的例項並快取起來,模組上的每一個方法也都生成了對應的RCTModuleMethod物件,這裡通過ModuleID和MethodID取到對應的Module例項和RCTModuleMethod例項進行呼叫。具體實現在_handleRequestNumber:moduleID:methodID:params:。
  6. RCTModuleMethod對JS傳過來的每一個引數進行處理。RCTModuleMethod可以拿到OC要呼叫的目標方法的每個引數型別,處理JS型別到目標型別的轉換,所有JS傳過來的數字都是NSNumber,這裡會轉成對應的int/long/double等型別,更重要的是會為block型別引數的生成一個block。
  7. OC模組方法呼叫完,執行block回撥。
  8. 呼叫到第6步說明的RCTModuleMethod生成的block。
  9. block裡帶著CallbackID和block傳過來的引數去調JS裡MessageQueue的方法invokeCallbackAndReturnFlushedQueue。
  10. MessageQueue通過CallbackID找到相應的JS callback方法。
  11. 呼叫callback方法,並把OC帶過來的引數一起傳過去,完成回撥。
    概況一下完整的流程如下:JS函式呼叫轉ModuleID/MethodID -> callback轉CallbackID -> OC根據ID拿到方法 -> 處理引數 -> 呼叫OC方法 -> 回撥CallbackID -> JS通過CallbackID拿到callback執行。

問題剖析

上述第4步留下一個問題,JS是怎樣把資料傳給OC,讓OC去調相應方法的?
答案是通過返回值。JS不會主動傳遞資料給OC,在調OC方法時,會在上述第4步把ModuleID,MethodID等資料加到一個佇列裡,等OC過來調JS的任意方法時,再把這個佇列返回給OC,此時OC再執行這個佇列裡要呼叫的方法。

相關推薦

React Native原生app通訊機制

概述 React Native用iOS自帶的JavaScriptCore作為JS的解析引擎,但並沒有用到JavaScriptCore提供的一些可以讓JS與OC互調的特性,而是自己實現了一套機制,這套機制可以通用於所有JS引擎上,在沒有JavaScriptCor

重新認識React NativeAndroid的通訊原理

此文基於react natve的 September 2018 - revision 5 版本 在我的上一篇文章《帶你徹底看懂React Native和Android原生控制元件之間的對映關係》中,我已經完整地剖析了從RN元件到原生控制元件之間的對映關係,文中簡單地提到了一些通訊原理,本文我就來詳細地講解一

React Native原生iOS Objective-C的互動解決方案

用一個RCTRootView作為iOS裡一個Controller的view。在RN層的左上角返回按鈕點選後pop回iOS層。發現無法執行,除錯發現controller的navigationCont的值是空的。發現與RN互動的這個self地址和iOS層的self並不是同一個

react native 中textInput的value屬性

hold eric 工作 als size 保持 chang 無奈 bsp TextInput用法就不多講了,主要記錄下遇到的一個怪問題。 背景:項目需要開發一個充值頁面,需要一個輸入框,然後幾個按鈕,輸入框是允許用戶自己輸入任意金額,按鈕是可以讓用戶快捷選擇金

JVM架構GC垃圾回收機制

JVM架構圖分析 下圖:參考網路+書籍,如有侵權請見諒 (想了解Hadoop記憶體溢位請看: JVM被分為三個主要的子系統 (1)類載入器子系統(2)執行時資料區(3)執行引擎 1. 類載入器子系統 Java的動態類載入功能是由類載入器子系統處理。當它在執行時(

React-Native 元件之 Modal的使用

Modal元件可以用來覆蓋包含React Native根檢視的原生檢視(如UIViewController,Activity),用它可以實現遮罩的效果。 屬性 Modal提供的屬性有: animationType(動畫型別) PropTypes.oneOf([‘none’, ‘slide’, ‘fade’]

React Native開發】React Native控制元件之RefreshControl元件(21)

轉載請標明出處:(一)前言         【好訊息】個人網站已經上線執行,後面部落格以及技術乾貨等精彩文章會同步更新,請大家關注收藏:http://www.lcode.org        今天我們一起來看一下RefreshControl下拉重新整理元件講解以及使用例項剛建立的React Native技術交

JVM GC的工作機制

一些基礎的知識結構和底層的原理性的東西還是需要好好進行研究的,這樣就更有助於理解 JAVA 的很多知識;本文章是在檢視<<深入理解Java 虛擬機器>>後所得心得,希望對大家有所幫助,也歡迎技術大咖批評指教; 一、JVM結構、記憶體分配、垃圾回收演算

React Native之屬性類型檢查機制 PropType 變成 prop-types

word man div color object platform UC 靜態 ESS 屬性確認的作用 使用 React Native 創建的組件是可以復用的,所以我們開發的組件可能會給項目組其他同事使用。但別人可能對這個組件不熟悉,常常會忘記使用某些屬性,或者某些屬性傳

帶你徹底看懂React NativeAndroid原生控制元件之間的對映關係

此文基於react natve的 September 2018 - revision 5 版本 本人學校畢業後就當了安卓爬坑專業戶,3年來總算爬習慣了,不料今年掉進了RN這個天坑,從此開始了我的悲慘人生。。。Anyway,RN的思想還是值得學習的,今天就從Android的角度開始分析一下react nati

React NativeiOS原生方法互動

原生傳遞引數給React Native 初始化時傳值 - (instancetype)initWithBundleURL:(NSURL *)bundleURL moduleName:(NSString *

React Native原生之間的通訊(iOS)

本文將講述下在原生和React Native之間的通訊方式。方式和邏輯綜合了自己的思維方式,主要參考了React Native中文官方文件,因為感覺它講的方式有些不妥,所以就按自己思路組織了下文。 雖然發覺一遍文章要把所有通訊方式講清楚不太科學,不過把思路講講倒是可以,

react native原生RN的互動

前言:前端時間隨著自己的學習和研究,也寫了幾篇關於react native的文章,雖然都是比較簡的,但是都是根據自己的效果來做的流程,所以還是比較實用的,可以避免很多的坑。這篇react native

React NativeAndroid整合

前言 按照React Native的迭代速度,使用官網的文件,已經不能很順利的實現React Native和Android的有效整合。React Native最新版本 已經是0.39。為了更好的講解React Native和Android的整合我這裡列出我本地

react-native 呼叫原生模組

 一,繼承 ReactContextBaseJavaModule 實現如下方法 自定義方法用 @ReactMethod註釋 /** * 日誌列印module * Created by ybj on 2016/2/26.

React-Native原生的3種互動通訊(Android)

前言 最近到新公司,採用React-Native開發App。在某些效能方面有問題或者模組特殊的開發情況,不可避免的需要我們原生開發(Android\IOS)給予前端開發支援。 在為前端書寫模組部分,不可避免的要接觸核心的通訊部分。 大致分為2種情況:

React Native編寫跨平臺APP

應用程序 主動 瀏覽器 橋接 con ram nsstring webview install 用React Native編寫跨平臺APP React Native 是一個編寫iOS與Android平臺實時、原生組件渲染的應用程序的框架。它基於R

React Native電商項目實戰混合APP開發 React Native實戰 混合APP實戰開發

mp4 實戰 nav 部分 ati nic 購物 獲取 面數據 React Native 和 angular+ionic 是目前網絡上最火的混合APP開發語言,其功能強大能夠開發出安卓和IOS程序! ------------------課程目錄--------------

h5做的app原生app的區別

個人 目前 sign 引用 enc 語言 缺陷 情況 支持 之所以說h5做的app和原生app的區別,是因為一位博友的問題: 隨著 h5 的普及,是不是不再需要開發 app ? 我的回答是要分業務需求,分場合而定。 比如現在的微信小程序這麽流行,甚至也取代了不少app,但是

React Native Android原生方向進階一

雖然說react native的設計初衷是為了敏捷開發,write once,run anywhere,但是還是開放了原生接入這一高階功能,而原生也是一位這個開發方向一個繞不過去的坎,今天先跑了一下流程,總結一下先 1、react-native init mengft_module