有時候App需要訪問平台API,但React Native可能還沒有相應的模塊包裝;或者你需要復用一些Java代碼,而不是用Javascript重新實現一遍;又或者你需要實現某些高性能的、多線程的代碼,譬如圖片處理、數據庫、或者各種高級擴展等等。
我們把React Native設計為可以在其基礎上編寫真正的原生代碼,並且可以訪問平台所有的能力。這是一個相對高級的特性,我們並不認為它應當在日常開發的過程中經常出現,但具備這樣的能力是很重要的。如果React Native還不支持某個你需要的原生特性,你應當可以自己實現該特性的封裝。
Toast模塊
本向導會用Toast作為例子。假設我們希望可以從Javascript發起一個Toast消息(Android中的一種會在屏幕下方彈出、保持一段時間的消息通知)
我們首先來創建一個原生模塊。一個原生模塊是一個繼承了ReactContextBaseJavaModule的Java類,它可以實現一些JavaScript所需的功能。我們這里的目標是可以在JavaScript里寫ToastAndroid.show('Awesome', ToastAndroid.SHORT);,來調起一個Toast通知。
ReactContextBaseJavaModule要求派生類實現getName方法。這個函數用於返回一個字符串名字,這個名字在JavaScript端標記這個模塊。這里我們把這個模塊叫做ToastAndroid,這樣就可以在JavaScript中通過React.NativeModules.ToastAndroid訪問到這個模塊。
注意:模塊名前的RCT前綴會被自動移除。所以如果返回的字符串為"RCTToastAndroid",在JavaScript端依然通過React.NativeModules.ToastAndroid訪問到這個模塊。
一個可選的方法getContants返回了需要導出給JavaScript使用的常量。它並不一定需要實現,但在定義一些可以被JavaScript同步訪問到的預定義的值時非常有用。
要導出一個方法給JavaScript使用,Java方法需要使用注解@ReactMethod。方法的返回類型必須為void。React Native的跨語言訪問是異步進行的,所以想要給JavaScript返回一個值的唯一辦法是使用回調函數或者發送事件
注冊模塊
在Java這邊要做的最后一件事就是注冊這個模塊。我們需要在應用的Package類的createNativeModules方法中添加這個模塊。如果模塊沒有被注冊,它也無法在JavaScript中被訪問到。
這個package需要在MainActivity.java文件的getPackages方法中提供。這個文件位於你的react-native應用文件夾的android目錄中。具體路徑是: android/app/src/main/java/com/your-app-name/MainActivity.java.
為了讓你的功能從JavaScript端訪問起來更為方便,通常我們都會把原生模塊封裝成一個JavaScript模塊。這不是必須的,但省下了每次都從NativeModules中獲取對應模塊的步驟。這個JS文件也可以用於添加一些其他JavaScript端實現的功能。
現在,在別處的JavaScript代碼中可以這樣調用你的方法:
var ToastAndroid = require('./ToastAndroid')
ToastAndroid.show('Awesome', ToastAndroid.SHORT);