React Native十四:原生模块

前端之家收集整理的这篇文章主要介绍了React Native十四:原生模块前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
有时候App需要访问API,但React Native可能还没有相应的模块包装;或者你需要复用一些Java代码,而不是用JavaScript重新实现一遍;又或者你需要实现某些够性能的、多线程的代码,譬如图片处理、数据库、或者各种高级扩展等等;
我们React Native设计为可以在其基础上编写真正代码的原生代码,并且可以访问平台的所有能力。这是一个相对高级的特性,我们并不认为它应当在日常开发过程中经常出现,但是具备这样的能力是很重要的。如果React Native还不支持某个你需要的原生特性,你应该可以自己实现该特性的封装。

一、Toast、Log模块
本向导会用Toast、Log作为例子,假设我们希望可以从JavaScript发起一个Toas消息(Andorid中的一种会在屏幕下方弹出、保持一段时间消息通知),打印Andorid的Logcat日志。
1.我们首先在项目的源码目录来创建一个原生的模块ToastModule.java,LogModule.java。一个原生的模块是一个集成了ReactCoontextBaseJavaModule的Java类,它可以实现一些JavaScript所需的功能

TostModule1.java类
public class TostModule1 extends ReactContextBaseJavaModule{
    private static final String DURATION_SHORT_KEY = "SHORT";
    private static final String DURATION_LONG_KEY = "LONG";

    public TostModule1(ReactApplicationContext reactContext) {
        super(reactContext);
    }

    //ReactContextBaseJavaModule要求派生类实现getName方法。这个函数用于返回一个字符串名字,这个名字在JavaScript端标记这个模块。这里我们把这个模块叫做Toast1,这样就可以在JavaScript中通过React.NativeModules.Toast1访问到这个模块。
    @Override
    public String getName() {
        return "Toast1";
    }

    //一个可选的方法getContants返回了需要导出给JavaScript使用的常量。它并不一定需要实现,但在定义一些可以被JavaScript同步访问到的预定义的值时非常有用。
    @Override
    public Map<String,Object> getConstants() {
        final Map<String,Object> constants = MapBuilder.newHashMap();
        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(final String message,final int duration) {
        UiThreadUtil.runOnUiThread(new Runnable() {
            @Override
            public void run(){
                Toast.makeText(getReactApplicationContext(),message,duration).show();
            }
        });
    }
}
LogModule.java类
public class LogModule extends ReactContextBaseJavaModule{

    public LogModule(ReactApplicationContext reactContext) {
        super(reactContext);
    }

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

    @ReactMethod
    public void d(String tag,String msg){
        Log.d(tag,msg);
    }
}
2.参数类型
3. 在Java这边要做的最后一件事就是注册这个模块。我们需要在应用的Package类的 createNativeModules 方法添加这个模块。如果模块没有被注册,它也无法在JavaScript中被访问到。
AppReactPackage.java类
public class AppReactPackage implements ReactPackage{

    @Override
    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
        List<NativeModule> modules = new ArrayList<>();
        modules.add(new LogModule(reactContext));
        modules.add(new TostModule1(reactContext));
        return modules;
    }

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

    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
        return Collections.emptyList();
    }
}
4.这个package需要在MainActivity.java文件注册提供。
MainActivity.java文件
public class MainActivity extends AppCompatActivity implements DefaultHardwareBackBtnHandler {
    private ReactRootView mReactRootView;
    private ReactInstanceManager mReactInstanceManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mReactRootView = new ReactRootView(this);
        mReactInstanceManager = ReactInstanceManager.builder()
                .setApplication(getApplication())
                .setBundleAssetName("index.android.bundle")
                .setJSMainModuleName("index.android")
                .addPackage(new MainReactPackage())
                .addPackage(new AppReactPackage())
                .setUseDeveloperSupport(BuildConfig.DEBUG)
                .setInitialLifecycleState(LifecycleState.RESUMED)
                .build();
        mReactRootView.startReactApplication(mReactInstanceManager,"MyAwesomeApp",null);

        setContentView(mReactRootView);
    }

    @Override
    public void invokeDefaultOnBackPressed() {
        super.onBackPressed();
    }

    @Override
    protected void onPause() {
        super.onPause();

        if (mReactInstanceManager != null) {
            mReactInstanceManager.onPause();
        }
    }

    @Override
    protected void onResume() {
        super.onResume();

        if (mReactInstanceManager != null) {
            mReactInstanceManager.onResume(this,this);
        }
    }

    @Override
    public void onBackPressed() {
        if (mReactInstanceManager != null) {
            mReactInstanceManager.onBackPressed();
        } else {
            super.onBackPressed();
        }
    }

    @Override
    public boolean onKeyUp(int keyCode,KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_MENU && mReactInstanceManager != null) {
            mReactInstanceManager.showDevOptionsDialog();
            return true;
        }
        return super.onKeyUp(keyCode,event);
    }
}
5.为了让你的功能从JavaScript端访问起来更为方便,通常我们都会把原生模块封装成一个JavaScript模块。这不是必须的,但省下了每次都从NativeModules中获取对应模块的步骤。这个JS文件也可以用于添加一些其他JavaScript端实现的功能。在你的 应用根目录下创建ToastMoudle.js文件
ToastAndroid.js文件
var { NativeModules } = require('react-native');
module.exports = NativeModules.ToastAndroid;
6.最后,我们这里的目标是可以在JavaScript里调用相关的API。
index.android.js文件
'use strict';
var React = require('react');
var ReactNative = require('react-native');
var ToastAndroid = require('./ToastAndroid');

var {
  Text,View,StyleSheet,AppRegistry,NativeModules,} = ReactNative;

var Log1 = NativeModules.Log;
Log1.d("Log1","LOG");

ToastAndroid.show("Tost1",ToastAndroid.SHORT);

class MyAwesomeApp extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.hello}>Hello,World</Text>
      </View>
    )
  }
}
var styles = StyleSheet.create({
  container: {
    flex: 1,justifyContent: 'center',},hello: {
    fontSize: 20,textAlign: 'center',margin: 10,});

AppRegistry.registerComponent('MyAwesomeApp',() => MyAwesomeApp);
7.运行App,并在LogCat中展示如下:


二、回调函数
原生模块还支持一种特殊的参数——回调函数。它提供了一个函数来把返回值传回给JavaScript。最典型的一个场景就是javascript层调用java层的网络请求方法,java层拿到网络数据后需要将结果返回给javascript层。下面我们就依次为实例,演示一下:
1.创建NetModule继承ReactContextBaseJavaModule,实现getName方法,返回值为Net,暴露一个getResult方法给javascript,并进行注解;
NetModule.java类
public class NetModule extends ReactContextBaseJavaModule {
    private static final String MODULE_NAME="Net";
    public NetModule(ReactApplicationContext reactContext) {
        super(reactContext);
    }

    @Override
    public String getName() {
        return MODULE_NAME;
    }

    @ReactMethod
    public void getResult(String url,final Callback callback){
        Log.e("TAG","正在请求数据");
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    String result="这是结果";
                    Thread.sleep(1000);
                    callback.invoke(true,result);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}
2. 在前面的AppReactPackage类createNativeModules函数注册该模块(同上忽略);
3.在JavaScript层新建一个Net.js文件文件修改index.android.js文件
Net.js文件
'use strict';
var { NativeModules } = require('react-native');
var RCTNet= NativeModules.Net;

var Net = {
  getResult: function (
    url: string,callback:Function,): void {
    RCTNet.getResult(url,callback);
  },};

module.exports = Net;
index.android.js文件
import React from 'react';
import {
    ..... ......
} from 'react-native';

var WebView=require('./RTCWebView');
var Net=require('./Net');
Net.getResult("http://baidu.com",(code,result)=>{
                                    console.log("callback",code,result);
                                 });
...... .....
AppRegistry.registerComponent('AwesomeProject',() => MyAwesomeApp);
4.Debug运行如下,在Chrome的Develop Tools中显示如下:

猜你在找的React相关文章