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。

package com.secondproject;

import android.widget.Toast;

import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;

import java.util.Map;
import java.util.HashMap;

public class MyToastModule extends ReactContextBaseJavaModule {

    private static final String DURATION_SHORT_KEY = "SHORT";
    private static final String DURATION_LONG_KEY = "LONG";

    public MyToastModule(ReactApplicationContext reactContext) {
      super(reactContext);
    }
    /**
     * getName方法。这个函数用于返回一个字符串名字,就是js中的模块名
     */
    @Override
    public String getName() {
      return "MyToast";
    }
    /**
     * 返回了需要导出给JavaScript使用的常量
     */
    @Override
    public Map<String, Object> 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;
    }
    /**
     * 导出给js使用的方法,需要使用注解@ReactMethod。方法的返回类型必须为void
     */
    @ReactMethod
    public void show(String message, int duration) {
      Toast.makeText(getReactApplicationContext(), message, duration).show();
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

注册模块

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

  • 编写类实现ReactPackage接口
  • 在当前应用的MainActivity.java中添加当前模块 
    我们先来看下系统的ReactPackage接口: 
    node_modules\react-native\ReactAndroid\src\main\java\com\facebook\react\ReactPackage.java
package com.facebook.react;

import java.util.List;

import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.ViewManager;

public interface ReactPackage {

  List<NativeModule> createNativeModules(ReactApplicationContext reactContext);


  List<Class<? extends JavaScriptModule>> createJSModules();


  List<ViewManager> createViewManagers(ReactApplicationContext reactContext);
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

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

  • MyReactPackage.java
package com.secondproject;

import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.List;
import java.util.Collections;

public class MyReactPackage implements ReactPackage {


  @Override
  public List<NativeModule> createNativeModules(
                              ReactApplicationContext reactContext) {
    List<NativeModule> modules = new ArrayList<>();
    // 这里的MyToastModule是之前他添加的module
    modules.add(new MyToastModule(reactContext));

    return modules;
  }

    @Override
    public List<Class<? extends JavaScriptModule>> createJSModules() {
        // TODO Auto-generated method stub
        return Collections.emptyList();
    }

    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
        // TODO Auto-generated method stub
        return Collections.emptyList();
    }

}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 在MainActivity中添加当前模块 
    可以看到在MainActivity的onCreate方法中,有这样一段代码:
mReactInstanceManager = ReactInstanceManager.builder()
                .setApplication(getApplication())
                .setBundleAssetName("index.android.bundle")
                .setJSMainModuleName("index.android")
                .addPackage(new MainReactPackage())
                .setUseDeveloperSupport(BuildConfig.DEBUG)
                .setInitialLifecycleState(LifecycleState.RESUMED)
                .build();
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

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

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

在javascript中使用原生模块

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

// 这里的MyToast就是在MyToastModule的getName中返回的module名称
React.NativeModules.MyToast.show('调用系统的toast啦', ToastAndroid.SHORT);
 
 
  • 1
  • 2
  • 1
  • 2
_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>
    );
  }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

现在效果如下: 
这里写图片描述

将原生模块封装

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

toast.js

'use strict';

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

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

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

参数类型

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

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

回调函数

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


package com.facebook.react.bridge;

public interface Callback {

  public void invoke(Object... args);

}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

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

新建CallbackModule

package com.secondproject;

import android.widget.Toast;

import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Callback;

import java.util.Map;
import java.util.HashMap;

public class CallbackModule extends ReactContextBaseJavaModule {


    public CallbackModule(ReactApplicationContext reactContext) {
      super(reactContext);
    }
    // 返回js中使用当前模块的名称
    @Override
    public String getName() {
      return "MyCallback";
    }

    @ReactMethod
    public void getAddResult(int number1, int number2, Callback callback) {
        try {
             int result = number1 + number2;
             //通过invoke方法将结果传递给js
             callback.invoke("结果是:",result);
        } catch (Exception e) {
             e.printStackTrace();
        }

    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

注册CallbackModule 模块

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

 @Override
  public List<NativeModule> createNativeModules(
                              ReactApplicationContext reactContext) {
    List<NativeModule> modules = new ArrayList<>();

    modules.add(new MyToastModule(reactContext));
    modules.add(new CallbackModule(reactContext));
    return modules;
  }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

创建callback.js

  • 在与index.android.js同目录下创建callback.js,用来导出当前CallbackModule 模块。
'use strict';

var { NativeModules } = require('react-native');
module.exports = NativeModules.MyCallback;
 
 
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

使用MyCallback

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

var MyToast = require('./toast');
var MyCallback = require('./callback');

MyCallback.getAddResult(3,2,(code,result)=>{
        console.log("callback",code,result);
        MyToast.show(code + result,ToastAndroid.SHORT);
 });  
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

此时效果如下: 
这里写图片描述

发送事件到JavaScript

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

 @ReactMethod
    public void getAddResult(int number1, int number2, Callback callback) {

        try {

              WritableMap params = Arguments.createMap();
              //通过WritableMap传递一个参数给js,也可以传递多个
              params.putString("first","i am first");
              getReactApplicationContext()
                      .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
                      .emit("toastMe", params);//对应的javascript层的事件名为toastMe,注册该事件即可进行回调 

             int result = number1 + number2;
             callback.invoke("结果是:",result);
        } catch (Exception e) {
             e.printStackTrace();
        }

    }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

在componentDidMount方法中注册事件

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值