從Android到React Native開發(二、通訊與模組實現)

戀貓de小郭發表於2019-02-28

大家吼,(◐‿◑)作為失蹤人口迴歸,這次第二期,就讓我們來懟React Native的通訊,快速實現單獨的React Native模組到APP裡,愉悅吧騷年。至於為什麼要有這期?當然是為了愉悅的飆車啦ε-(´∀`; )。

下方包含原始碼劇透,劇情略長,請緊張耐心的往下看。( ̄^ ̄)ゞ
文中標註有“【數字】”的是乾貨喲。

從Android到React Native開發(二、通訊與模組實現)
準備好接受新姿勢了麼

開始之前

 本文前上部分主要拆解一些基礎的原理,由淺到深;後半部分講解整合模組實現,你也可以直接閱讀後半部分,快速實現模組整合。文中著重在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端互動。

從Android到React Native開發(二、通訊與模組實現)
開始目瞪口呆

一、上半部分

1、MainApplication

 預設react-native init建立的專案裡,會有一個MainActivity和一個MainApplicationMainApplication繼承了ReactApplication介面,介面只有一個方法:getReactNativeHost

1.1、ReactNativeHost

 這個介面實現在Application,通過getApplication,你可以隨時拿到ReactNativeHost,它會幫你建立一個單例:ReactInstanceManager作為管理器。ReactNativeHost還可以配置一系列的行為,其中最關鍵的,便是getPackages介面。

從Android到React Native開發(二、通訊與模組實現)
預設在Application實現了ReactNativeHost

getPackages 介面返回了一系列的ReactPackage類,ReactPackage可以看作是,向ReactNative註冊了原生模組,這樣在JS中你也可以使用原生模組的功能,按鍵第三方庫時,react-native link命令,其中一個行為,就是在getPackages中幫你插入,庫需要引用到的模組。

從Android到React Native開發(二、通訊與模組實現)

 如上圖,是MainReactPackage內部實現,MainReactPackage是官方的類,其中關聯了很多NativeModule,Module中你可以通過@ReactMethod註解,指定一個方法為JS可以呼叫的方法,如下圖的DetailModule,便是繼承了NativeModule的JAVA端實現類,在js中引入。

從Android到React Native開發(二、通訊與模組實現)

總結一下,劃重點Σ( ̄。 ̄ノ)ノ:
  • 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幫你實現。

從Android到React Native開發(二、通訊與模組實現)

首先我們直接來分析下順序:

  • 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端監聽頁面的狀態,會同時收到兩個訊息通知。

 再深入的我們就先不追究,後面有深入通訊相關的文章推薦,其中涉及到CatalystInstanceReactBridgeBridgeCallback等等,通過jni轉為字串,再拼接為命令和程式碼執行等原理,有興趣的可以移步吸幾口。

 可以看出,ReactInstanceManager是其中的關鍵,無論哪裡都有它的身影,ReactNativeHost的Package列表是給它,建立ReactContex也是它,其實載入JS的也是它,所以後半部分實現模組,其中很關鍵的就是它了。

從Android到React Native開發(二、通訊與模組實現)

二、下半部分

實現一個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。

從Android到React Native開發(二、通訊與模組實現)

1.2、建立一個ReactInstanceManager,配置你需要支援的自定義選項,最後通過build(),實現一個XReactInstanceManagerImpl,將它這是給ReactRootView。

從Android到React Native開發(二、通訊與模組實現)

如上圖,可以看到:

  • setJSBundleFile,你可以指定載入bundle檔案的路徑
  • addPackge,增加你的React Native小程式支援的原生模組,其中MainReactPackage是必須的。
  • setJSMainModuleName指定了主js模組的名字。

 是不是很簡單,這樣你就可以通過原生的http,去下載和更新js bundle,然後載入顯示,從而實現類似微信小程式的需求。

從Android到React Native開發(二、通訊與模組實現)

 當然,如上圖,不要忘記給你的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。

從Android到React Native開發(二、通訊與模組實現)

  • BackAndroid.App()通過下圖中的原生module,最終經過幾次變換,會呼叫到上面Activity的DefaultHardwareBackBtnHandler介面,通過invokeDefaultOnBackPressed()響應。

從Android到React Native開發(二、通訊與模組實現)

  • 最後在invokeDefaultOnBackPressed中通過 super.onBackPressed();結束Activity的一生。

【4】綜合理解,React Native對於android back按鍵,是在onBackPressed中,把所有的back事件都發到js端,如果js端沒監聽,或者監聽都返回了false,那麼就會回到繼承了DefaultHardwareBackBtnHandler介面,實現了invokeDefaultOnBackPressed的Activity處理。

2、建立你的Moudle實現自定義互動

(˶‾᷄ ⁻̫ ‾᷅˵)下方乾貨滿滿,請耐心吸食

 首先我們建立一個DetailMoudle繼承ReactContextBaseJavaModule,如下圖。

從Android到React Native開發(二、通訊與模組實現)

  • 通過getName指定了js端使用的名字。

  • 通過@ReactMethod註解指定了哪些方法可以被js端呼叫,js端可以傳遞指定型別的引數,這裡注意【5】@ReactMethod的返回型別一定是void

  • 引數傳遞js端與android端對應如下圖。

    從Android到React Native開發(二、通訊與模組實現)
  • Callback/Promise 都是回撥介面,promise有更多元化的回撥選擇。但是注意:【6】無論是Callback 還是 Promise ,在執行invoke/(reject、resolve)之後,都會在js的訊息佇列中被銷燬,不能再呼叫一次,也就是說所有的callback只能執行一次。

  • 你還可以通過訊息機制實現android和js端的互動,如下圖。

從Android到React Native開發(二、通訊與模組實現)

  • 如下圖,通過繼承ActivityEventListener,用ReactApplicationContext新增監聽,可以方便的在module中監聽activity返回。網上說的用訊息阻塞佇列的做法就算了吧。

從Android到React Native開發(二、通訊與模組實現)

  • 通過如下方法,可以在android的其他位置拿到module物件。

從Android到React Native開發(二、通訊與模組實現)

  • 建立一個DetailPackage 繼承 ReactPackage,將建立好的DetailModule新增到createNativeModules方法中,如下圖。

從Android到React Native開發(二、通訊與模組實現)

  • 最後將你的ReactPackage新增到你的ReactNativeHost或者ReactInstanceManager中。在js端通過下圖方式呼叫。

從Android到React Native開發(二、通訊與模組實現)

 歐耶,終於碼完了,你是不是對於React Native 相關的通訊機制,還有互動實現有了新的瞭解呢?如果你覺得還不滿足,這裡推薦一個深度瞭解React Native通訊的系列。文中從android到js端,還有jni層面都做了詳細的跟蹤,有興趣的可跳轉觀摩,下方連結。

React-Native系列Android——Native與Javascript通訊原理

專案相關的原始碼:github.com/CarGuo/Lear…

個人github:github.com/CarGuo

從Android到React Native開發(二、通訊與模組實現)
還記得我麼

相關文章