使用Toast做爲例子。實現的功能是能夠在JavaScript裏寫ToastAndroid.show('Awesome', ToastAndroid.SHORT)
來顯示一個Toast通知。javascript
代碼:https://github.com/future-cha...html
建立一個類,繼承ReactContextBaseJavaModule
。java
public class ToastModule extends ReactContextBaseJavaModule { private static final String DURATION_SHORT_KEY = "SHORT"; private static final String DURATION_LONG_KEY = "LONG"; public ToastModule(ReactApplicationContext reactContext) { super(reactContext); } }
以後須要實現一個方法getName
。react
@Override public String getName() { return "AnotherToastAndroid"; // 不能返回ToastAndroid,這個會報錯,或者須要手動指定覆蓋RN已有的實現。 }
這個方法必須實現。它的返回值是React Native的js部分調用模塊時的名稱。另外,若是這個方法返回的字符串包含RCT的話,那麼RCT會被去掉。也就是,若是getName
返回的是RCTToastAndroid
的話,在js調用的時候仍是使用ToastAndroid
。android
接下來實現show
方法。git
@ReactMethod public void show(String message, int duration) { Toast.makeText(getReactApplicationContext(), message, duration).show(); }
注意:模塊要導出方法給js使用,那麼這個方法上必須使用@ReactMethod
註解!而且返回值必須爲void
。若是要返回值的話,須要使用回調方法或者註冊事件。這些下文會講到。github
在導出給js的方法中添加參數的時候,只能使用部分類型(java -> javascript):react-native
Boolean -> Bool Integer -> Number Double -> Number Float -> Number String -> String Callback -> function ReadableMap -> Object ReadableArray -> Array
註冊模塊以後就可使用。若是你的App裏沒有Package類,那就本身建立一個。好比本例,就能夠建立名爲ToastReactPackage
的Package類,該類實現ReactPackage
接口。promise
public class ToastReactPackage implements ReactPackage { @Override public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) { return null; } @Override public List<Class<? extends JavaScriptModule>> createJSModules() { return null; } @Override public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) { return null; } }
類中每一個方法的名稱已經明確的代表了其自己的做用。咱們這裏導出的是一個模塊,因此須要實現createNativeModules
方法。其餘的方法只要返回一個空列表就能夠。最後的ToastReactPackage
類的實現是:async
public class ToastReactPackage implements ReactPackage { @Override public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) { List<NativeModule> modules = new ArrayList<>(); modules.add(new ToastModule(reactContext)); return modules; } @Override public List<Class<? extends JavaScriptModule>> createJSModules() { return Collections.emptyList(); } @Override public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) { return Collections.emptyList(); } }
最後在MainApplication
的getPackages
方法裏註冊Package。
public class MainApplication extends Application implements ReactApplication { private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { @Override public boolean getUseDeveloperSupport() { return BuildConfig.DEBUG; } @Override protected List<ReactPackage> getPackages() { return Arrays.<ReactPackage>asList( new MainReactPackage(), new ToastReactPackage() // 這一句用來註冊咱們的AnotherToastAndroid模塊 ); } };
在React Native中使用模塊。
import { //... NativeModules, PixelRatio, } from 'react-native'; let AnotherToastAndroid = NativeModules.AnotherToastAndroid; export default class mobike extends Component { render() { return ( <View style={styles.container}> <TouchableOpacity style={styles.button} onPress={() => { AnotherToastAndroid.show('Another Toast', AnotherToastAndroid.LONG); }}> <Text style={{ textAlign: 'center', }}> Show Toast </Text> </TouchableOpacity> </View> ); } }
不直接相關的內容就隱藏掉了。使用的時候只要在import中引入NativeModules
,以後在let AnotherToastAndroid = NativeModules.AnotherToastAndroid;
提取咱們的原生模塊。這個模塊的名字就是在Android模塊getName
方法裏返回的名稱AnotherToastAndroid
。
以後在TouchableOpacity的onPress
事件中調用AnotherToastAndroid
的show
方法。
至此,咱們以前說的功能就都實現了。
前面的內容要運行起來還差這個一環。返回常量,你看到js代碼裏有這樣的調用:
AnotherToastAndroid.show('Another Toast', AnotherToastAndroid.LONG);
有這麼一句:AnotherToastAndroid.LONG
。要使用LONG,還有沒有用到的SHORT常量,須要原生模塊返回這樣的常量。
@Nullable @Override public Map<String, Object> getConstants() { // return super.getConstants(); final Map<String, Object> constants = new HashMap<>(); constants.put(DURATION_SHORT_KEY, Toast.LENGTH_SHORT); constants.put(DURATION_LONG_KEY, Toast.LENGTH_LONG); return constants; }
方法getConstants
是類ReactContextBaseJavaModule
的一個可選方法,專門用來返回常量。返回的內容就是字典Map<String, Object>
。
如今Demo能夠運行起來了。
前文說道,要返回值給js就須要用回調方法。如今看看在原生裏如何實現這一點:
@ReactMethod public void currentThreadName(Callback errorCallback, Callback successCallback) { try { String tn = Thread.currentThread().getName(); successCallback.invoke(tn); } catch(Exception e) { errorCallback.invoke(e.getMessage()); } }
在Toast模塊里加了一個獲取當前線程的方法。Android的這個導出回調方法看起來仍是有點奇怪。原本應該是一個回調返回兩個參數:一個error,一個結果。這裏用了兩個Callback
,可能也是條件限制吧。
看看js如何使用:
<Button style={{ marginTop: 10, }} title='use callback' pressHandler={ () => { AnotherToastAndroid.currentThreadName((msg) => console.log(`error message ${msg}`) , (threadName) => { Alert.alert('Thread Name', `thread nane: ${threadName}`, null); }); }} />
回調缺點很明顯。因此多數的時候都會選擇使用Promise。再加上如今流行的async-await
就更多的人使用Promise了。
@ReactMethod public void currentThreadNameByPromise(Promise promise) { try { String tn = Thread.currentThread().getName(); promise.resolve(tn); } catch (Exception e) { promise.reject("Thread Error", e); } }
來看看如何使用Promise的:
<Button style={{ marginTop: 10, }} title='use Promise' pressHandler={ () => { AnotherToastAndroid.currentThreadNameByPromise().then((threadName) => Alert.alert('Thread Name', `thread nane: ${threadName}`, null) ).catch(err => Alert.alert('Thread Name', `get thread nane error: ${err.message}`, null)); }} />
這些知識在通常的使用中就足夠了,若是須要更復雜的內容能夠查看官方文檔。我也會在以後補齊這部分的內容。