從Android到ReactNative開發(二、通訊與模組實現)
大家吼,(◐‿◑)作為失蹤人口迴歸,這次第二期,就讓我們來懟React Native的通訊,快速實現單獨的React Native模組到APP裡,愉悅吧騷年。至於為什麼要有這期?當然是為了愉悅的飆車啦ε-(´∀`; )。
下方包含原始碼劇透,劇情略長,請緊張耐心的往下看。( ̄^ ̄)ゞ
文中標註有“【數字】”的是乾貨喲。
開始之前
本文前上部分主要拆解一些基礎的原理,由淺到深;後半部分講解整合模組實現,你也可以直接閱讀後半部分,快速實現模組整合。文中著重在Android端幫助大家理解React Native。
下方先提前介紹一些關鍵類。
- ReactActivity:預設所有的Activity都繼承它。
- ReactNativeHost :幫你”hold”住ReactInstanceManager。
- ReactActivityDelegate:ReactActivity的邏輯代理實現。
- ReactRootView :React NativeUI的所在。
- ReactInstanceManager:React Native的扛把子,抽象類。
- XReactInstanceManagerImpl :ReactInstanceManager的實現類。
- ReactContext: 管理React Native的狀態等。
- NativeModule:繼承它的module可以在js端使用,其中就包括有DeviceEventManagerModule,與JS實現事件模式互動的module。
- Callback/Promise: 回撥介面,與js端互動。
一、上半部分
1、MainApplication
預設react-native init
建立的專案裡,會有一個MainActivity
和一個MainApplication
。MainApplication
繼承了ReactApplication
介面,介面只有一個方法:getReactNativeHost
。
1.1、ReactNativeHost
這個介面實現在Application,通過getApplication,你可以隨時拿到ReactNativeHost
,它會幫你建立一個單例:ReactInstanceManager
作為管理器。ReactNativeHost
還可以配置一系列的行為,其中最關鍵的,便是getPackages
介面。
getPackages
介面返回了一系列的ReactPackage類,ReactPackage可以看作是,向ReactNative註冊了原生模組,這樣在JS中你也可以使用原生模組的功能,按鍵第三方庫時,react-native link
命令,其中一個行為,就是在getPackages中幫你插入,庫需要引用到的模組。
如上圖,是MainReactPackage
內部實現,MainReactPackage
是官方的類,其中關聯了很多NativeModule
,Module中你可以通過@ReactMethod
註解,指定一個方法為JS可以呼叫的方法,如下圖的DetailModule
,便是繼承了NativeModule
的JAVA端實現類,在js中引入。
總結一下,劃重點Σ( ̄。 ̄ノ)ノ:
- MainApplication繼承了ReactApplication,返回了ReactNativeHost。
- ReactNativeHost裡建立了ReactInstanceManager,並且實現了getPackages,返回了ReactPackage列表。
- ReactInstanceManager在建立Builder時,把ReactPackage列表加入到管理器。
- ReactPackage列表裡面都關聯了NativeModule的實現類。
- NativeModule的實現類可以通過註解,類似@ReactMethod讓原生方法可以被React呼叫。
粗略流程:
MainApplication -> ReactApplication -> ReactNativeHost -> ReactInstanceManager -> ReactPackage -> NativeModule -> CatalystInstance(這位就是負責傳送的同志)
【1】所以只要實現了ReactPackage和NativeModule,將它註冊到ReactNativeHost
或者ReactInstanceManager
,就可以在React Native中繼承你原生的模組了。
2、ReactActivity
MainActivity大家肯定不陌生,預設react-native init
建立的專案裡,MainActivity十分簡單,只有一個getMainComponentName
,它就是告訴Avtivity,預設需要載入的js元件名(Component)是什麼,而其餘的事情,都是繼承的ReactActivity
幫你實現。
首先我們直接來分析下順序:
-
ReactActivity
預設建立了一個ReactActivityDelegate
。 -
ReactActivityDelegate
建立了一個單例的ReactInstanceManager
(通過上面的ReactNativeHost)。 -
ReactInstanceManager
(抽象類)內部建立了ReactRootView
。 -
ReactInstanceManager
的實現類為XReactInstanceManagerImpl
。 -
XReactInstanceManagerImpl
在createReactContext 建立了ReactApplicationContext
。 -
ReactApplicationContext
實現了生命狀態事件的分發,通知js端Activity的狀態。
結合上面 MainApplication部分:
-
ReactInstanceManager
裡面註冊了ReactPackage
。 -
ReactPackage
關聯了NativeModule
的實現類。 -
NativeModule
可以通過增加註解的方法被JS端呼叫。
所以流程可以粗略認為是
1、MainApplication -> ReactApplication -> ReactNativeHost。
2、ReactActivity -> ReactActivityDelegate -> ReactNativeHost ->
ReactInstanceManager -> ReactContex -> ReactPackage -> NativeModule
例如,ReactActivity的OnResume事件流程:
1、ReactActivityDelegate.onResume();。
2、getReactNativeHost().getReactInstanceManager().onHostResume();。
3、ReactContext.onHostResume();。
4、AppStateModule.onHostResume();。
5、RCTDeviceEventEmitter 通過 emit(“appStateDidChange”, createAppStateEventMap());通知js。
【2】這裡我們需要注意,只要繼承了ReactActivity,無論你實現了多少個Activity,它們的內部ReactInstanceManager都只有一個,訊息會出現共享的情況。比如A頁面onResume是,B頁面就會onPause,如果你在JS端監聽頁面的狀態,會同時收到兩個訊息通知。
再深入的我們就先不追究,後面有深入通訊相關的文章推薦,其中涉及到CatalystInstance
、ReactBridge
、BridgeCallback
等等,通過jni轉為字串,再拼接為命令和程式碼執行等原理,有興趣的可以移步吸幾口。
可以看出,ReactInstanceManager是其中的關鍵,無論哪裡都有它的身影,ReactNativeHost的Package列表是給它,建立ReactContex也是它,其實載入JS的也是它,所以後半部分實現模組,其中很關鍵的就是它了。
二、下半部分
實現一個React Native應用,有兩種方法:
1、一種直接繼承ReactActivity
,指定js中需要載入的元件名字。
2、在佈局中加入ReactRootView
,通過ReactInstanceManager
載入管理js。
關於第一種,我們不深入展開,因為它的實現通過上面已經大致講完,參考init下來的react工程,可以很簡單的實現,他們共享Applicaton中的ReactNativeHost,和Host建立的ReactInstanceManager。
那麼我們為什麼要講第二種呢?這裡首先講解一個知識點:
【3】React Native在打包的時候,是把js程式碼打包成js bundle,js bundle就是壓縮後的js程式碼,它放在android的assert檔案下,啟動React Native應用時預設載入它。
既然如何,那麼我們是否可以修改js bundle的載入路徑?當然可以啊,不然說個卵(╯‵□′)╯︵┻━┻。通過網路下載不同的js bundle,載入實現不同的React Native App,哇塞,這不就是簡單的微信小程式麼。
ReactNativeHost也可以配置js bundle的檔案路徑,那麼繼承ReactActivity不是可以更簡單的實現嗎?不,因為繼承ReactActivity,他們內部共享了一個ReactInstanceManager,作為單獨的React Native程式模組,想想訊息、路由、store等等會互相干擾汙染·····
1、建立一個React Native 應用。
1.1、如下圖,首先你需要在佈局中建立一個ReactRootView。
1.2、建立一個ReactInstanceManager,配置你需要支援的自定義選項,最後通過build(),實現一個XReactInstanceManagerImpl,將它這是給ReactRootView。
如上圖,可以看到:
- setJSBundleFile,你可以指定載入bundle檔案的路徑
- addPackge,增加你的React Native小程式支援的原生模組,其中MainReactPackage是必須的。
- setJSMainModuleName指定了主js模組的名字。
是不是很簡單,這樣你就可以通過原生的http,去下載和更新js bundle,然後載入顯示,從而實現類似微信小程式的需求。
當然,如上圖,不要忘記給你的Activity繼承DefaultHardwareBackBtnHandler介面,還有將activity的生命狀態通知到js端。
1.3 DefaultHardwareBackBtnHandler
這裡要大篇幅講解下,DefaultHardwareBackBtnHandler介面,通過它我們可以整體瞭解,React Native從android端,到JS端對back按鍵事件的處理。
- 首先Activity需要繼承DefaultHardwareBackBtnHandler介面。DefaultHardwareBackBtnHandler只有一個invokeDefaultOnBackPressed方法。
- ReactInstanceManager在
onHostResume(Activity activity, DefaultHardwareBackBtnHandler defaultBackButtonImpl);
中需要傳入activity和handler介面。 - ReactInstanceManager.onBackPressed()會通過DeviceEventManagerModule,向js端傳送了
"hardwareBackPress"
訊息。 - JS中,在BackAndrod類中,預設通過全域性靜態方法,註冊了
"hardwareBackPress"
的監聽。如下圖所示,監聽中判斷全域性Set表中的callBack,倒序迴圈判斷,是否有callback,callback是否返回true,如果都沒有,就呼叫exitApp。
- BackAndroid.App()通過下圖中的原生module,最終經過幾次變換,會呼叫到上面Activity的DefaultHardwareBackBtnHandler介面,通過invokeDefaultOnBackPressed()響應。
- 最後在invokeDefaultOnBackPressed中通過 super.onBackPressed();結束Activity的一生。
【4】綜合理解,React Native對於android back按鍵,是在onBackPressed中,把所有的back事件都發到js端,如果js端沒監聽,或者監聽都返回了false,那麼就會回到繼承了DefaultHardwareBackBtnHandler介面,實現了invokeDefaultOnBackPressed的Activity處理。
2、建立你的Moudle實現自定義互動
(˶‾᷄ ⁻̫ ‾᷅˵)下方乾貨滿滿,請耐心吸食
首先我們建立一個DetailMoudle繼承ReactContextBaseJavaModule,如下圖。
- 通過
getName
指定了js端使用的名字。 - 通過
@ReactMethod
註解指定了哪些方法可以被js端呼叫,js端可以傳遞指定型別的引數,這裡注意【5】@ReactMethod的返回型別一定是void。 - 引數傳遞js端與android端對應如下圖。
- Callback/Promise 都是回撥介面,promise有更多元化的回撥選擇。但是注意:【6】無論是Callback 還是 Promise ,在執行invoke/(reject、resolve)之後,都會在js的訊息佇列中被銷燬,不能再呼叫一次,也就是說所有的callback只能執行一次。
- 你還可以通過訊息機制實現android和js端的互動,如下圖。
- 如下圖,通過繼承ActivityEventListener,用ReactApplicationContext新增監聽,可以方便的在module中監聽activity返回。網上說的用訊息阻塞佇列的做法就算了吧。
- 通過如下方法,可以在android的其他位置拿到module物件。
- 建立一個DetailPackage 繼承 ReactPackage,將建立好的DetailModule新增到
createNativeModules
方法中,如下圖。
- 最後將你的ReactPackage新增到你的ReactNativeHost或者ReactInstanceManager中。在js端通過下圖方式呼叫。
歐耶,終於碼完了,你是不是對於React Native 相關的通訊機制,還有互動實現有了新的瞭解呢?如果你覺得還不滿足,這裡推薦一個深度瞭解React Native通訊的系列。文中從android到js端,還有jni層面都做了詳細的跟蹤,有興趣的可跳轉觀摩,下方連結。
React-Native系列Android——Native與Javascript通訊原理
專案相關的原始碼:https://github.com/CarGuo/LearnProject
RN完整學習專案:https://github.com/CarGuo/GSYGithubAPP
個人github:https://github.com/CarGuo
相關文章
- 從Android到React Native開發(二、通訊與模組實現)AndroidReact Native
- 從0到1實現一個模組間通訊的服務元件元件
- 從Android到ReactNative開發(三、自定義原生控制元件支援)AndroidReact控制元件
- Android 即時通訊開發小結(二)Android
- Android技術分享| 視訊通話開發流程(二)Android
- ReactNative呼叫Android原生模組ReactAndroid
- APP 開發從 0 到 1(二)框架與網路APP框架
- 從module的簡單實現到模組化
- 阿里Android開發規範:程式、執行緒與訊息通訊阿里Android執行緒
- Android模組化改造以及模組化通訊框架Android框架
- Android端實現多人音視訊聊天應用(二):多人視訊通話Android
- 參考微信模組化通訊具體實現
- electron開發入門(二)程式通訊
- Android中通過Messenger與Service實現程式間雙向通訊AndroidMessenger
- Android模組化開發實踐Android
- 【nodejs原理&原始碼賞析(5)】net模組與通訊的實現NodeJS原始碼
- JAVA通訊(二)——實現客戶機和伺服器通訊Java伺服器
- 【Android開發VR實戰】二.播放360°全景視訊AndroidVR
- ReactNative填坑之旅–與Native通訊之iOS篇ReactiOS
- xmpp實現的即時通訊聊天(二)
- 我是如何從通訊轉到Java軟體開發工程師的?Java工程師
- 從零到一:用ReactNative開發的第一個跨平臺appReactAPP
- C++ Qt開發:QUdpSocket實現組播通訊C++QTUDP
- 【GMT43智慧液晶模組】例程二:串列埠通訊實驗串列埠
- 從開發小白到音視訊專家
- 日程功能模組【從建模到程式碼實現】UML + JavaFXJava
- Flutter與webview通訊橋樑開發FlutterWebView
- android藍芽BLE(二) —— 通訊Android藍芽
- 模組化開發(二)
- ReactNative原始碼篇:通訊機制React原始碼
- Android Spingboot 實現SSE通訊案例Androidboot
- iOS實現XMPP通訊(二)XMPP程式設計iOS程式設計
- Flutter混合開發(三):Android與Flutter之間通訊詳細指南FlutterAndroid
- 《iOS遊戲開發:從創意到實現》小編推介iOS遊戲開發
- Android通過繼承Binder類實現多程式通訊Android繼承
- Android開發之執行緒間通訊Android執行緒
- 快速Android開發系列通訊篇之EventBusAndroid
- 驅動開發:透過應用堆實現多次通訊