React Native十四:原生模块

来源:互联网 发布:动漫图软件 编辑:程序博客网 时间:2024/05/21 17:39
有时候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中显示如下:

1 0
原创粉丝点击