美文网首页RN
React Native调用原生模块

React Native调用原生模块

作者: 沐风雨木 | 来源:发表于2017-06-27 11:20 被阅读574次

    本篇主要参考中文网链接 ,不同的是,把我自己当时接入感到迷惑的地方,详细说一下。

    有时候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通知。

    这里的原生模块,是在RN项目的android文件夹下创建,如下图:
    这里的android文件夹可以用Android Studio打开,之后编辑,自我感觉方便的很。

    Paste_Image.png Paste_Image.png
    package com.facebook.react.modules.toast;
    
    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;
    
    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);
      }
    }
    

    ReactContextBaseJavaModule要求派生类实现getName方法。这个函数用于返回一个字符串名字,这个名字在JavaScript端标记这个模块。这里我们把这个模块叫做ToastAndroid,这样就可以在JavaScript中通过React.NativeModules.ToastAndroid访问到这个模块。译注:RN已经内置了一个名为ToastAndroid的模块,所以如果你在练习时完全照抄,那么运行时会报错名字冲突!所以请在这里选择另外一个名字!

      @Override
      public String getName() {
        return "ToastAndroidDemo";//我已经改了
      }
    

    译注:模块名前的RCT前缀会被自动移除。所以如果返回的字符串为"RCTToastAndroidDemo",在JavaScript端依然可以通过React.NativeModules.ToastAndroidDemo访问到这个模块。

    一个可选的方法getContants返回了需要导出给JavaScript使用的常量。它并不一定需要实现,但在定义一些可以被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;
      }
    

    要导出一个方法给JavaScript使用,Java方法需要使用注解@ReactMethod。方法的返回类型必须为void。React Native的跨语言访问是异步进行的,所以想要给JavaScript返回一个值的唯一办法是使用回调函数或者发送事件(参见下文的描述)。

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

    全部代码:

    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);
        }
    
        @Override
        public String getName() {
            return "ToastAndroidDemo";
        }
    
        @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;
        }
    
        @ReactMethod
        public void show(String message, int duration) {
            Toast.makeText(getReactApplicationContext(), message, duration).show();
        }
    }
    

    参数类型

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

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

    参阅ReadableMapReadableArray

    注册模块

    在Java这边要做的最后一件事就是注册这个模块。我们需要在应用的Package类的createNativeModules方法中添加这个模块。如果模块没有被注册,它也无法在JavaScript中被访问到。

    package com.facebook.react.modules.toast;
    
    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 AnExampleReactPackage implements ReactPackage {
    
      @Override
      public List<Class<? extends JavaScriptModule>> createJSModules() {
        return Collections.emptyList();
      }
    
      @Override
      public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
        return Collections.emptyList();
      }
    
      @Override
      public List<NativeModule> createNativeModules(
                                  ReactApplicationContext reactContext) {
        List<NativeModule> modules = new ArrayList<>();
    
        modules.add(new ToastModule(reactContext));
    
        return modules;
      }
    

    这个package需要在MainApplication.java文件的getPackages方法中提供。这个文件位于你的react-native应用文件夹的android目录中。具体路径是: android/app/src/main/java/com/your-app-name/MainApplication.java

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

    为了让你的功能从JavaScript端访问起来更为方便,通常我们都会把原生模块封装成一个JavaScript模块。这不是必须的,但省下了每次都从NativeModules中获取对应模块的步骤。这个JS文件也可以用于添加一些其他JavaScript端实现的功能。
    ToastAndroidDemo.js文件代码:

    'use strict';
    
    /**
     * This exposes the native ToastAndroid module as a JS module. This has a function 'show'
     * which takes the following parameters:
     *
     * 1. String message: A string with the text to toast
     * 2. int duration: The duration of the toast. May be ToastAndroid.SHORT or ToastAndroid.LONG
     */
    import { NativeModules } from 'react-native';
    
    // 下一句中的ToastAndroid即对应上文
    // public String getName()中返回的字符串
    // 练习时请务必选择另外的名字!
    
    export default NativeModules.ToastAndroidDemo;
    

    现在,在别处的JavaScript代码中可以这样调用你的方法:

    import ToastAndroidDemo from './ToastAndroidDemo';
    ToastAndroidDemo.show('Awesome', ToastAndroidDemo.SHORT);
    

    相关文章

      网友评论

        本文标题:React Native调用原生模块

        本文链接:https://www.haomeiwen.com/subject/olnscxtx.html