React Native调用原生组件
在React Native开发过程中,有时候我们可能需要访问平台的API,但react Native还没有相应的实现,或者是React Native还不支持一些原生的属性,我们需要调用原生代码来实现,或者是我们需要复用一些原来的Java代码,这个时候我们就需要创建一个原生模块来自己实现对我们需要功能的封装。 相关文档可以参照官方的介绍。
实例
下面我们就通过实现一个自定义模块,来熟悉编写原生模块需要用的一些知识。该模块主要实现调用一些Android原生的功能,比如弹Toast,启动Activity等。
实现模块
首先来创建一个原生模块。一个原生模块是一个继承了 ReactContextBaseJavaModule 的Java类,它有一个必须实现的方法getName(),它返回一个字符串名字,在js中我们就使用这个名字调用这个模块;还有构造函数NativeModule。
public class MyNativeModule extends ReactContextBaseJavaModule {
private final static String MODULE_NAME = "MyNativeModule";
private static final String TestEvent = "TestEvent";
private ReactApplicationContext mContext;
public MyNativeModule(ReactApplicationContext reactContext) {
super(reactContext);
mContext = reactContext;
}
@Override
public String getName() {
return MODULE_NAME;
}
@Nullable
@Override
public Map<String, Object> getConstants() {
final Map<String, Object> constants = new HashMap<>();
constants.put("SHORT", Toast.LENGTH_SHORT);
constants.put("LONG", Toast.LENGTH_LONG);
constants.put("NATIVE_MODULE_NAME", MODULE_NAME);
constants.put(TestEvent, TestEvent);
return constants;
}
@ReactMethod
public void startActivity(){
Intent intent = new Intent(mContext,SecondActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
}
@ReactMethod
public void showToast(String msg, int duration){
Toast.makeText(mContext, msg, duration).show();
}
}
这里需要对React Native和原生的类型映射做一个简单的介绍。详细的还可以参考ReadableMap和ReadableArray
Boolean -> Bool
Integer -> Number
Double -> Number
Float -> Number
String -> String
Callback -> function
ReadableMap -> Object
ReadableArray -> Array
例如,实现getContants方法导出需要给JavaScript使用的常量。
@Nullable
@Override
public Map<String, Object> getConstants() {
final Map<String, Object> constants = new HashMap<>();
constants.put("SHORT", Toast.LENGTH_SHORT);
constants.put("LONG", Toast.LENGTH_LONG);
constants.put("NATIVE_MODULE_NAME", MODULE_NAME);
constants.put(TestEvent, TestEvent);
return constants;
}
注册模块
接下来我们需要向系统注册这个模块,通过实现ReactPackage接口来实现。代码如下:
public class MyReactPackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new MyNativeModule(reactContext));
return modules;
}
@Override
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
添加模块
在Application的getPackages()方法中添加上面的模块。
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
//添加的模块
new MyReactPackage()
);
}
或者这MainActivity的onCreate中,添加如下代码:
@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 MyReactPackage())
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();
mReactRootView.startReactApplication(mReactInstanceManager, "HelloWorld", null);
setContentView(mReactRootView);
}
Js端封装模块
为了使javascript端访问起来更为方便,通常我们都会把原生模块封装成一个JavaScript模块。
import { NativeModules } from 'react-native';
// 这里的MyNativeModule必须对应
// public String getName()中返回的字符串
export default NativeModules.MyNativeModule;
接下来,就可以直接使用了。
import MyNativeModule from './MyNativeModule';
class HelloWorld extends React.Component {
startActivity(){
console.log("MODULE NAME: ",MyNativeModule.NATIVE_MODULE_NAME);
MyNativeModule.startActivity();
}
showToast(){
console.log("MODULE NAME: ",MyNativeModule.NATIVE_MODULE_NAME);
MyNativeModule.showToast("From JS", MyNativeModule.LONG);
}
render() {
return (
<View style={styles.container}>
<TouchableOpacity onPress={this.startActivity}>
<Text style={styles.hello}>start Activity</Text>
</TouchableOpacity>
</View>
)
}
}
其他知识
React Native的跨语言访问是异步进行的,所以想要给JavaScript返回一个值的唯一办法是使用回调函数或者发送事件。
回调函数
原生模块还支持一种特殊的参数——回调函数。它提供了一个函数来把返回值传回给JS。
@ReactMethod
public void testCallback(int para1, int para2, Callback resultCallback){
int result = para1 + para2;
resultCallback.invoke(result);
}
也可以在JS中调用。例如:
testCallback(){
MyNativeModule.testCallback(100,100,(result) => {
console.log("result: ",result); //'result: ', 200
});
}
原生模块通常只应调用回调函数一次。但是,它可以保存callback并在将来调用。 callback并非在对应的原生函数返回后立即被执行——注意跨语言通讯是异步的,这个执行过程会通过消息循环来进行。
RCTDeviceEventEmitter
生模块可以在没有被调用的情况下往JavaScript发送事件通知。最简单的办法就是通过RCTDeviceEventEmitter,这可以通过ReactContext来获得对应的引用。RCTDeviceEventEmitter相当于客户端的广播机制。
public void sendEvent(){
WritableMap params = Arguments.createMap();
params.putString("module", "MyNativeModule");
mContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(TestEvent, params);
}
在JS中调用的代码如下:
import { DeviceEventEmitter } from 'react-native';
......
componentWillMount() {
console.log("componentWillMount");
//接收事件
DeviceEventEmitter.addListener(MyNativeModule.TestEvent, info => {
console.log(info);
});
}
startActivityForResult
如果需要监听activity的生命周期事件(比如onResume, onPause等等),模块必须实现LifecycleEventListener,然后需要在构造函数中注册一个监听函数。
public MyNativeModule(ReactApplicationContext reactContext) {
super(reactContext);
mContext = reactContext;
//添加监听
reactContext.addLifecycleEventListener(this);
}
实现LifecycleEventListener的几个接口。
@Override
public void onHostResume() {
Log.e(MODULE_NAME, "onHostResume");
}
@Override
public void onHostPause() {
Log.e(MODULE_NAME, "onHostPause");
}
@Override
public void onHostDestroy() {
Log.e(MODULE_NAME, "onHostDestroy");
}
- 【Java学习笔记之八】JavaBean中布尔类型使用注意事项
- BZOJ 1597: [Usaco2008 Mar]土地购买【斜率优化+凸包维护】
- BZOJ 1046: [HAOI2007]上升序列【贪心+二分状态+dp+递归】
- 【Java学习笔记之九】java二维数组及其多维数组的内存应用拓展延伸
- BZOJ 1293: [SCOI2009]生日礼物【单调队列】
- Javascript缓存投毒学习与实战
- 【Java学习笔记之十】Java中循环语句foreach使用总结及foreach写法失效的问题
- Codeforces 839B Game of the Rows【贪心】
- Codeforces 839A Arya and Bran【暴力】
- 【Java学习笔记之十一】Java中常用的8大排序算法详解总结
- 浅谈zip格式处理逻辑漏洞
- C/C++中peek函数的原理及应用
- 洛谷 P1200 [USACO1.1]你的飞碟在这儿Your Ride Is He…【字符串+模拟】
- 洛谷 P1055 ISBN号码【字符串+模拟】
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 这可能是你见过最好的Redis主从复制原理
- 移动直播集成问题
- TRTC功能咨询
- TKE集群pod镜像拉取失败定位思路
- Android内存管理(JVM 、DVM(dalvik) 、ART简单介绍)
- WordPress 站点地址被恶意篡改的防护方案讨论
- 猿实战06——不一样的地址管理
- Redis常用命令详解
- three.js 制作逻辑转体游戏(下)
- ROS机器人TF基础(坐标相关概念和实践)
- (在模仿中精进数据可视化01) 全国38城居住自由指数可视化
- js字符串/数组常用方法总结
- ThinkPHP5+mpdf 实现富文本生成 PDF文件
- nodejs使用readline逐行读取和写入文件
- go语言逐行读取和写入文件