您的位置:首页 > Web前端 > React

React Native十四:原生模块

2016-06-12 23:39 381 查看
有时候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中显示如下:
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息