react native学习笔记25——Android原生模块的封装与调用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/teagreen_red/article/details/79180452

前言

之前我们学习了React Native的部分api,可以看到React Native为我们封装了非常丰富的api,即使如此有时候我们的应用需要进行访问原生平台系统的api接口,但是React Native可能还没有封装相应功能组件或api, 这种情况我们可以自己封装原生平台的组件。本文将介绍对Android原生组件封装,使我们能在React Native中调用原生模块。下面以我们在Android原生开发中常见的Toast为例,进行封装之后,通过React Native中的JavaScript来进行弹出toast消息。

创建模块类

首先需要创建一个原生模块类,该原生模块类是继承ReactContextBaseJavaModule类的Java代码,它可以实现JavaScript所需的一些功能。我们的目标是使用JavaScript代码通过调用ToastModuleAndroid.show(‘Awesome’,ToastModuleAndroid.SHORT)方法显示一个toast消息提醒。
在项目的ExerciseProject\android\app\src\main\java\com\exerciseproject目录下新建一个java文件ToastModule.java, 该类继承ReactContextBaseJavaModule。

package com.exerciseproject;

import android.widget.Toast;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;

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

public class ToastModule extends ReactContextBaseJavaModule  {

    private static final String DURATION_SHORT="SHORT";
    private static final String DURATION_LONG="LONG";
    public ToastModule(ReactApplicationContext reactContext) {
        super(reactContext);
    }

    @Override
    public String getName() {
        return "ToastModule";
    }

    @Override
    public Map<String, Object> getConstants() {
        final Map<String, Object> constants = new HashMap<>();
        constants.put(DURATION_SHORT, Toast.LENGTH_SHORT);
        constants.put(DURATION_LONG, Toast.LENGTH_LONG);
        return constants;
    }

    @ReactMethod
    public void show(String message, int duration) {
        Toast.makeText(getReactApplicationContext(), message, duration).show();
    }
}

这里实现了四个方法:ToastModule、getName、getConstants、show。

ToastModule是构造方法,在Java中与类名名称相同的方法称为构造方法,通常用来初始化。这里只需通过 super(reactContext)调用父类ReactContextBaseJavaModule的构造方法,不需要额外的工作。

getName返回一个字符串信息,它决定了今后在JavaScript中使用什么名称来调用该模块。这里我们将该模块命名为”ToastModule”。

RN已经内置了一个名为ToastAndroid的模块,所以在练习时请勿使用ToastAndroid的名字,否则运行时会报错名字冲突!

    @Override
    public String getName() {
        return "ToastModule";
    }

getContants是一个可选的方法,返回了需要导出给JavaScript使用的常量。它并不一定需要实现,但在定义一些可以被JavaScript同步访问到的预定义的值时非常有用。

    @Override
    public Map<String, Object> getConstants() {
        final Map<String, Object> constants = new HashMap<>();
        constants.put(DURATION_SHORT, Toast.LENGTH_SHORT);
        constants.put(DURATION_LONG, Toast.LENGTH_LONG);
        return constants;
    }

show方法是我们自定义的方法,要导出一个方法给JavaScript使用,需要添加@ReactMethod注解。方法的返回类型必须为void。React Native的跨语言访问是异步进行的,所以想要给JavaScript返回一个值的唯一办法是使用回调函数或者发送事件。

    @ReactMethod
    public void show(String message, int duration) {
        Toast.makeText(getReactApplicationContext(), message, duration).show();
    }

参数类型说明

上面我们讲解到@ReactMethod注解的方法,在Java和JavaScript中的数据类型有一些区别,下面的参数类型在@ReactMethod注明的方法中,会被直接映射到它们对应的JavaScript类型。总体对应方式如下:

Boolean -> Bool
Integer -> Number
Double -> Number
Float -> Number
String -> String
Callback -> function
ReadableMap -> Object
ReadableArray -> Array
参阅ReadableMapReadableArray

模块注册

前面我们完成了组件模块的定义以及提供对外方法的封装,下面还需要在Java这边进行注册该模块。我们需要一个实现ReactPackage的子类。
在ToastModule.java同一目录下新建一个ToastModulePackage.java文件。

package com.exerciseproject;

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

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class ToastModulePackage implements ReactPackage {
    @Override
    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
        List<NativeModule> modules = new ArrayList<>();
        modules.add(new ToastModule(reactContext));
        return modules;
    }


    public List<Class<? extends JavaScriptModule>> createJSModules() {
        return Collections.emptyList();
    }

    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
        return Collections.emptyList();
    }
}

该类基本是一个模板,需要实现createNativeModules、createJSModules、createViewManagers这三个方法,React Native用这些方法来决定需要导出哪些模块。这里需要注意,在createNativeModule方法中添加前面自定义的那个模块ToastModule。如果没有注册,那么当前模块在JavaScript中是无法被访问到。

modules.add(new ToastModule(reactContext));

接着这个package需要在MainApplication.java文件的getPackages方法中提供。MainApplication位于ExerciseProject\android\app\src\main\java\com\exerciseproject\MainApplication.java。之前在讲解数据库的配置时也有修改过MainApplication的getPackages方法。

    @Override
    protected List<ReactPackage> getPackages() {
      return Arrays.<ReactPackage>asList(
          new MainReactPackage(),
          new ToastModulePackage()  // <-- 添加这一行,类名替换成你的Package类的名字.
      );
    }

在JavaScript端使用

为了使当前封装的模块在JavaScript中可以方便使用,通常会把该原生模块封装成JavaScript模块。这样可以省下每次访问组件都要加入NativeModules的步骤,不过该步骤不是必须的。
本例中在\ExerciseProject\src\12_nativeapi\中新建一个ToastModuleAndroid.js文件

let { NativeModules } = require('react-native');
module.exports = NativeModules.ToastModule;

然后在别的JavaScript文件代码中就可以如下进行访问:

import ToastModuleAndroid from './ToastModuleAndroid';
ToastModuleAndroid.show("调用Android ToastModule弹出消息",ToastModuleAndroid.SHORT);

具体在JavaScript文件中的调用方法实例代码如下:

import React, { Component } from 'react';
import {
    AppRegistry,
    StyleSheet,
    Text,
    View,
    TouchableHighlight,
} from 'react-native';

import ToastModuleAndroid from './ToastModuleAndroid';

export default class ModulesDemo extends Component {
    render() {
        return (
            <View>
                <TouchableHighlight
                    style={styles.button}
                    underlayColor="#a5a5a5"
                    onPress={()=>ToastModuleAndroid.show("调用Android ToastModule弹出消息",ToastModuleAndroid.SHORT)}>
                    <Text style={styles.buttonText}>自定义Toast</Text>
                </TouchableHighlight>
            </View>
        );
    }
}
const styles = StyleSheet.create({
    button: {
        margin:5,
        backgroundColor: 'white',
        padding: 15,
        borderBottomWidth: StyleSheet.hairlineWidth,
        borderBottomColor: '#cdcdcd',
    },
});

最后看看运行效果:

示例源码:git地址

猜你喜欢

转载自blog.csdn.net/teagreen_red/article/details/79180452