react native使用原生模块

前端之家收集整理的这篇文章主要介绍了react native使用原生模块前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

有时候App需要访问平台API,但React Native可能还没有相应的模块包装;或者你需要复用一些Java代码,而不是用Javascript重新实现一遍;又或者你需要实现某些高性能的、多线程的代码,譬如图片处理、数据库、或者各种高级扩展等等。

使用步骤 ##

在RN中使用系统原生模块需要如下步骤:

  • 创建一个原生模块。一个原生模块是一个继承了ReactContextBaseJavaModule的Java类,派生实现getName 返回一个字符串名字,这个名字在JavaScript端标记这个模块
  • 一个可选的方法getContants返回了需要导出给JavaScript使用的常量。它并不一定需要实现,但在定义一些可以被JavaScript同步访问到的预定义的值时非常有用
  • 导出一个方法给JavaScript使用,Java方法需要使用注解@ReactMethod。方法的返回类型必须为void
  • 注册模块。我们需要在应用的Package类的createNativeModules方法添加这个模块

编写模块

下面,我以官方的demo为栗子,来显示一个在js中调用android原生toast的demo,首先需要编写一个类继承自ReactContextBaseJavaModule。

  1. package com.secondproject;
  2.  
  3. import android.widget.Toast;
  4.  
  5. import com.facebook.react.bridge.NativeModule;
  6. import com.facebook.react.bridge.ReactApplicationContext;
  7. import com.facebook.react.bridge.ReactContext;
  8. import com.facebook.react.bridge.ReactContextBaseJavaModule;
  9. import com.facebook.react.bridge.ReactMethod;
  10.  
  11. import java.util.Map;
  12. import java.util.HashMap;
  13.  
  14. public class MyToastModule extends ReactContextBaseJavaModule {
  15.  
  16. private static final String DURATION_SHORT_KEY = "SHORT";
  17. private static final String DURATION_LONG_KEY = "LONG";
  18.  
  19. public MyToastModule(ReactApplicationContext reactContext) {
  20. super(reactContext);
  21. }
  22. /** * getName方法。这个函数用于返回一个字符串名字,就是js中的模块名 */
  23. @Override
  24. public String getName() {
  25. return "MyToast";
  26. }
  27. /** * 返回了需要导出给JavaScript使用的常量 */
  28. @Override
  29. public Map<String,Object> getConstants() {
  30. final Map<String,Object> constants = new HashMap<>();
  31. constants.put(DURATION_SHORT_KEY,Toast.LENGTH_SHORT);
  32. constants.put(DURATION_LONG_KEY,Toast.LENGTH_LONG);
  33. return constants;
  34. }
  35. /** * 导出给js使用的方法,需要使用注解@ReactMethod。方法的返回类型必须为void */
  36. @ReactMethod
  37. public void show(String message,int duration) {
  38. Toast.makeText(getReactApplicationContext(),message,duration).show();
  39. }
  40. }

注册模块

上面已经编写好了导出给js使用的模块,下面还需要注册当前模块,如果模块没有被注册,它也无法在JavaScript中被访问到。注册模块份两步:

  • 编写类实现ReactPackage接口
  • 在当前应用的MainActivity.java中添加当前模块
    我们先来看下系统的ReactPackage接口:
    node_modules\react-native\ReactAndroid\src\main\java\com\facebook\react\ReactPackage.java
  1. package com.facebook.react;
  2.  
  3. import java.util.List;
  4.  
  5. import com.facebook.react.bridge.NativeModule;
  6. import com.facebook.react.bridge.ReactApplicationContext;
  7. import com.facebook.react.bridge.JavaScriptModule;
  8. import com.facebook.react.uimanager.UIManagerModule;
  9. import com.facebook.react.uimanager.ViewManager;
  10.  
  11. public interface ReactPackage {
  12.  
  13. List<NativeModule> createNativeModules(ReactApplicationContext reactContext);
  14.  
  15.  
  16. List<Class<? extends JavaScriptModule>> createJSModules();
  17.  
  18.  
  19. List<ViewManager> createViewManagers(ReactApplicationContext reactContext);
  20. }

可以看到这里有三个方法需要实现,其中,当前最关心的就是createNativeModules方法的实现。

  • MyReactPackage.java
  1. package com.secondproject;
  2.  
  3. import com.facebook.react.bridge.NativeModule;
  4. import com.facebook.react.bridge.ReactApplicationContext;
  5. import com.facebook.react.ReactPackage;
  6. import com.facebook.react.bridge.JavaScriptModule;
  7. import com.facebook.react.uimanager.ViewManager;
  8. import java.util.ArrayList;
  9. import java.util.List;
  10. import java.util.Collections;
  11.  
  12. public class MyReactPackage implements ReactPackage {
  13.  
  14.  
  15. @Override
  16. public List<NativeModule> createNativeModules(
  17. ReactApplicationContext reactContext) {
  18. List<NativeModule> modules = new ArrayList<>();
  19. // 这里的MyToastModule是之前他添加的module
  20. modules.add(new MyToastModule(reactContext));
  21.  
  22. return modules;
  23. }
  24.  
  25. @Override
  26. public List<Class<? extends JavaScriptModule>> createJSModules() {
  27. // TODO Auto-generated method stub
  28. return Collections.emptyList();
  29. }
  30.  
  31. @Override
  32. public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
  33. // TODO Auto-generated method stub
  34. return Collections.emptyList();
  35. }
  36.  
  37. }
  • 在MainActivity中添加当前模块
    可以看到在MainActivity的onCreate方法中,有这样一段代码
  1. mReactInstanceManager = ReactInstanceManager.builder()
  2. .setApplication(getApplication())
  3. .setBundleAssetName("index.android.bundle")
  4. .setJSMainModuleName("index.android")
  5. .addPackage(new MainReactPackage())
  6. .setUseDeveloperSupport(BuildConfig.DEBUG)
  7. .setInitialLifecycleState(LifecycleState.RESUMED)
  8. .build();

在当前代码里已经添加了一个package,.addPackage(new MainReactPackage()),这里将自己之前定义的MyReactPackage 同样加进去即可:

  1. mReactInstanceManager = ReactInstanceManager.builder()
  2. .setApplication(getApplication())
  3. .setBundleAssetName("index.android.bundle")
  4. .setJSMainModuleName("index.android")
  5. .addPackage(new MainReactPackage())
  6. .addPackage(new MyReactPackage())
  7. .setUseDeveloperSupport(BuildConfig.DEBUG)
  8. .setInitialLifecycleState(LifecycleState.RESUMED)
  9. .build();

在javascript中使用原生模块

使用也很简单,只需要这样一句代码就可以了:

  1. // 这里的MyToast就是在MyToastModule的getName中返回的module名称
  2. React.NativeModules.MyToast.show('调用系统的toast啦',ToastAndroid.SHORT);
  1. _onPress: function(duration,content) { React.NativeModules.MyToast.show('调用系统的toast啦',ToastAndroid.SHORT); },render: function() { return ( <View style={{flexDirection: 'column'}}> <Text onPress={()=> this._onPress(ToastAndroid.SHORT,'this is short')} style={styles.button}>点击调用原生API</Text> </View> ); }

现在效果如下:

将原生模块封装

为了在js里使用方便,可以将原生模块封装成一个JavaScript模块,在index.android.js同目录下新建一个toast.js文件

toast.js

  1. 'use strict';
  2.  
  3. var { NativeModules } = require('react-native');
  4. // 这里的MyToast就是在MyToastModule 的getName返回的模块名
  5. module.exports = NativeModules.MyToast;

此时使用起来就很方便了,如下:

  1. // 引入当前的模块,注意toast是文件
  2. var MyToast = require('./toast');
  3. // 这里的MyToast是toast.js文件通过module.exports给我们的
  4. MyToast.show('调用系统的tdsaf',ToastAndroid.SHORT);
@H_502_434@参数类型

下面的参数类型在@ReactMethod注明的方法中,会被直接映射到它们对应的JavaScript类型

  1. Boolean -> Bool
  2. Integer -> Number
  3. Double -> Number
  4. Float -> Number
  5. String -> String
  6. Callback -> function
  7. ReadableMap -> Object
  8. ReadableArray -> Array

回调函数

有时候,在js中调用android中自定义模块中的方法,需要有返回值,此时就需要使用到RN为我们封装好的一个Callback接口。

  1. package com.facebook.react.bridge;
  2.  
  3. public interface Callback {
  4.  
  5. public void invoke(Object... args);
  6.  
  7. }

可以看到,通过我们在java中调用invoke方法,将结果返回给js的,返回的参数类型和个数都是不限的。

新建CallbackModule

  1. package com.secondproject;
  2.  
  3. import android.widget.Toast;
  4.  
  5. import com.facebook.react.bridge.NativeModule;
  6. import com.facebook.react.bridge.ReactApplicationContext;
  7. import com.facebook.react.bridge.ReactContext;
  8. import com.facebook.react.bridge.ReactContextBaseJavaModule;
  9. import com.facebook.react.bridge.ReactMethod;
  10. import com.facebook.react.bridge.Callback;
  11.  
  12. import java.util.Map;
  13. import java.util.HashMap;
  14.  
  15. public class CallbackModule extends ReactContextBaseJavaModule {
  16.  
  17.  
  18. public CallbackModule(ReactApplicationContext reactContext) {
  19. super(reactContext);
  20. }
  21. // 返回js中使用当前模块的名称
  22. @Override
  23. public String getName() {
  24. return "MyCallback";
  25. }
  26.  
  27. @ReactMethod
  28. public void getAddResult(int number1,int number2,Callback callback) {
  29. try {
  30. int result = number1 + number2;
  31. //通过invoke方法将结果传递给js
  32. callback.invoke("结果是:",result);
  33. } catch (Exception e) {
  34. e.printStackTrace();
  35. }
  36.  
  37. }
  38. }

注册CallbackModule 模块

记得在之前的MyReactPackage中注册当前模块:

  1. @Override
  2. public List<NativeModule> createNativeModules(
  3. ReactApplicationContext reactContext) {
  4. List<NativeModule> modules = new ArrayList<>();
  5.  
  6. modules.add(new MyToastModule(reactContext));
  7. modules.add(new CallbackModule(reactContext));
  8. return modules;
  9. }

创建callback.js

  • 在与index.android.js同目录下创建callback.js,用来导出当前CallbackModule 模块。
  1. 'use strict';
  2.  
  3. var { NativeModules } = require('react-native');
  4. module.exports = NativeModules.MyCallback;

使用MyCallback

MyCallback的使用和MyToast相同,这里我将两者结合起来使用,代码如下:

  1. var MyToast = require('./toast');
  2. var MyCallback = require('./callback');
  3.  
  4. MyCallback.getAddResult(3,2,(code,result)=>{ console.log("callback",code,result); MyToast.show(code + result,ToastAndroid.SHORT); });

此时效果如下:

发送事件到JavaScript

另外,原生的代码也可以在没有被调用的情况下向js发送事件通知。通过RCTDeviceEventEmitter来实现。这里依然以刚才的CallbakModule为例:

  1. @ReactMethod
  2. public void getAddResult(int number1,Callback callback) {
  3.  
  4. try {
  5.  
  6. WritableMap params = Arguments.createMap();
  7. //通过WritableMap传递一个参数给js,也可以传递多个
  8. params.putString("first","i am first");
  9. getReactApplicationContext()
  10. .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
  11. .emit("toastMe",params);//对应的javascript层的事件名为toastMe,注册该事件即可进行回调
  12.  
  13. int result = number1 + number2;
  14. callback.invoke("结果是:",result);
  15. } catch (Exception e) {
  16. e.printStackTrace();
  17. }
  18.  
  19. }

在componentDidMount方法注册事件

  1. componentDidMount:function(){
  2. //使用DeviceEventEmitter注册事件
  3. DeviceEventEmitter.addListener('toastMe',(e)=>{
  4. MyToast.show(e.first,ToastAndroid.SHORT);
  5. });
  6. },

这里注册的事件后,一旦调用getAddResult方法,就会回到toastMe方法。此时效果如下:

ok,今天就到这里了,由于代码较大,这里我将用到的代码打包上传

猜你在找的React相关文章