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

你們吼,(◐‿◑)做爲失蹤人口迴歸,此次第二期,就讓咱們來懟React Native的通訊,快速實現單獨的React Native模塊到APP裏,愉悅吧騷年。至於爲何要有這期?固然是爲了愉悅的飆車啦ε-(´∀`; )。react

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

準備好接受新姿式了麼

開始以前

 本文前上部分主要拆解一些基礎的原理,由淺到深;後半部分講解集成模塊實現,你也能夠直接閱讀後半部分,快速實現模塊集成。文中着重在Android端幫助你們理解React Native。git

下方先提早介紹一些關鍵類。github

  • 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和一個MainApplicationMainApplication繼承了ReactApplication接口,接口只有一個方法:getReactNativeHost小程序

1.一、ReactNativeHost

 這個接口實如今Application,經過getApplication,你能夠隨時拿到ReactNativeHost,它會幫你建立一個單例:ReactInstanceManager做爲管理器。ReactNativeHost還能夠配置一系列的行爲,其中最關鍵的,即是getPackages接口。微信小程序

默認在Application實現了ReactNativeHost

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

 如上圖,是MainReactPackage內部實現,MainReactPackage是官方的類,其中關聯了不少NativeModule,Module中你能夠經過@ReactMethod註解,指定一個方法爲JS能夠調用的方法,以下圖的DetailModule,即是繼承了NativeModule的JAVA端實現類,在js中引入。promise

總結一下,劃重點Σ( ̄。 ̄ノ)ノ:
  • 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中繼承你原生的模塊了網絡

二、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端調用。

因此流程能夠粗略認爲是
一、MainApplication -> ReactApplication -> ReactNativeHost。
二、ReactActivity -> ReactActivityDelegate -> ReactNativeHost ->
ReactInstanceManager -> ReactContex -> ReactPackage -> NativeModule

例如,ReactActivity的OnResume事件流程:
一、ReactActivityDelegate.onResume();。
二、getReactNativeHost().getReactInstanceManager().onHostResume();。
三、ReactContext.onHostResume();。
四、AppStateModule.onHostResume();。
五、RCTDeviceEventEmitter 經過 emit("appStateDidChange", createAppStateEventMap());通知js。

【2】這裏咱們須要注意,只要繼承了ReactActivity,不管你實現了多少個Activity,它們的內部ReactInstanceManager都只有一個,消息會出現共享的狀況。好比A頁面onResume是,B頁面就會onPause,若是你在JS端監聽頁面的狀態,會同時收到兩個消息通知。

 再深刻的咱們就先不追究,後面有深刻通訊相關的文章推薦,其中涉及到CatalystInstanceReactBridgeBridgeCallback等等,經過jni轉爲字符串,再拼接爲命令和代碼執行等原理,有興趣的能夠移步吸幾口。

 能夠看出,ReactInstanceManager是其中的關鍵,不管哪裏都有它的身影,ReactNativeHost的Package列表是給它,建立ReactContex也是它,其實加載JS的也是它,因此後半部分實現模塊,其中很關鍵的就是它了。

2、下半部分

實現一個React Native應用,有兩種方法:
一、一種直接繼承ReactActivity,指定js中須要加載的組件名字。
二、在佈局中加入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等等會互相干擾污染·····  

一、建立一個React Native 應用。

1.一、以下圖,首先你須要在佈局中建立一個ReactRootView。

1.二、建立一個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處理。

二、建立你的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通訊原理

項目相關的源碼:github.com/CarGuo/Lear…

我的github:github.com/CarGuo

還記得我麼
相關文章
相關標籤/搜索