IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    React Native十四:原生模块

    summer发表于 2016-06-12 16:04:32
    love 0
    有时候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中显示如下:




沪ICP备19023445号-2号
友情链接