你們吼,(◐‿◑)做爲失蹤人口迴歸,此次第二期,就讓咱們來懟React Native的通訊,快速實現單獨的React Native模塊到APP裏,愉悅吧騷年。至於爲何要有這期?固然是爲了愉悅的飆車啦ε-(´∀`; )。react
下方包含源碼劇透,劇情略長,請緊張耐心的往下看。( ̄^ ̄)ゞ
文中標註有「【數字】」的是乾貨喲。android
本文前上部分主要拆解一些基礎的原理,由淺到深;後半部分講解集成模塊實現,你也能夠直接閱讀後半部分,快速實現模塊集成。文中着重在Android端幫助你們理解React Native。git
下方先提早介紹一些關鍵類。github
默認react-native init
建立的項目裏,會有一個MainActivity
和一個MainApplication
。MainApplication
繼承了ReactApplication
接口,接口只有一個方法:getReactNativeHost
。小程序
這個接口實如今Application,經過getApplication,你能夠隨時拿到ReactNativeHost
,它會幫你建立一個單例:ReactInstanceManager
做爲管理器。ReactNativeHost
還能夠配置一系列的行爲,其中最關鍵的,即是getPackages
接口。微信小程序
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 -> ReactInstanceManager -> ReactPackage -> NativeModule -> CatalystInstance(這位就是負責發送的同志)微信
【1】因此只要實現了ReactPackage和NativeModule,將它註冊到ReactNativeHost
或者ReactInstanceManager
,就能夠在React Native中繼承你原生的模塊了。網絡
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端監聽頁面的狀態,會同時收到兩個消息通知。
再深刻的咱們就先不追究,後面有深刻通訊相關的文章推薦,其中涉及到CatalystInstance
、ReactBridge
、BridgeCallback
等等,經過jni轉爲字符串,再拼接爲命令和代碼執行等原理,有興趣的能夠移步吸幾口。
能夠看出,ReactInstanceManager是其中的關鍵,不管哪裏都有它的身影,ReactNativeHost的Package列表是給它,建立ReactContex也是它,其實加載JS的也是它,因此後半部分實現模塊,其中很關鍵的就是它了。
實現一個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等等會互相干擾污染·····
1.一、以下圖,首先你須要在佈局中建立一個ReactRootView。
1.二、建立一個ReactInstanceManager,配置你須要支持的自定義選項,最後經過build(),實現一個XReactInstanceManagerImpl,將它這是給ReactRootView。
如上圖,能夠看到:
是否是很簡單,這樣你就能夠經過原生的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。
【4】綜合理解,React Native對於android back按鍵,是在onBackPressed中,把全部的back事件都發到js端,若是js端沒監聽,或者監聽都返回了false,那麼就會回到繼承了DefaultHardwareBackBtnHandler接口,實現了invokeDefaultOnBackPressed的Activity處理。
(˶‾᷄ ⁻̫ ‾᷅˵)下方乾貨滿滿,請耐心吸食
首先咱們建立一個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端的交互,以下圖。
createNativeModules
方法中,以下圖。歐耶,終於碼完了,你是否是對於React Native 相關的通訊機制,還有交互實現有了新的瞭解呢?若是你以爲還不知足,這裏推薦一個深度瞭解React Native通訊的系列。文中從android到js端,還有jni層面都作了詳細的跟蹤,有興趣的可跳轉觀摩,下方連接。
React-Native系列Android——Native與Javascript通訊原理
項目相關的源碼:github.com/CarGuo/Lear…
我的github:github.com/CarGuo