赞
踩
前段时间做了个ReactNative的App,发现ReactNative中不少组件并不存在,所以还是需要自己对原生模块进行编写让JS调用,正是因为在这个编写过程中遇到不少问题,发觉了官网文档中许多的不足。所以产生了写一个实践教程的想法,最终有了这么一篇文章。
整篇文章主要以编写一个原生模块为例子,来讲述了我们在编写原生模块所用到的一些知识,并且在整个例子中,配有了完整的实践代码,方便大家理解并调试。除了这些内容,文章还讲述了我们如何将自己编写的原生模块发布到npm上分享给别人使用。希望能够给大家带来帮助,也希望大家将自己编写的原生模块分享出来。
准备工作
我们需要先创建一个ReactNative工程,使用如下命令创建。
1
$ react
native init TestProject
创建好工程之后,我们使用android
studio打开TestProject/android/下的android工程。
之后,点击android studio菜单上的File
——> New ——> New Module,之后选择Android
Library,设置模块名和包名,我的设置如下:
点击完成之后,我们的安卓项目中就多了个名字为nativemoduleexample模块。
之后,我们在app工程中的build.gradle文件中的dependencies添加一行compile
project(':nativemoduleexample'),让主工程app依赖我们新创建的Library。
最后,我们还需要让新创建的Library依赖react
native,和上面差不多,只需要在我们新创建的nativemoduleexample下的build.gradle中的dependencies添加一行compile
"com.facebook.react:react-native:+"就行了。
一、编写原生模块代码
####1、创建原生模块,并进行注册
我们首先点击打开前面创建的nativemoduleexample库,在src/main/; border-radius: 5px; border-width: 0.1px;">src/main/java/后面跟上就是前面设置的包名。
在这个目录下,我们首先创建一个原生模块BGNativeExampleModule,它继承于ReactContextBase; border-radius: 5px; border-width: 0.1px;">BGNativeExampleModule。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package
com.liuchungui.nativemoduleexample;
import
com.facebook.react.bridge.NativeModule;
import
com.facebook.react.bridge.ReactApplicationContext;
import
com.facebook.react.bridge.ReactContext;
import
com.facebook.react.bridge.ReactContextBaseJavaModule;
import
com.facebook.react.bridge.ReactMethod;
import java.lang.String;
public class BGNativeExampleModule
extends ReactContextBaseJavaModule
{
public BGNativeExampleModule(ReactApplicationContext
reactContext) {
super(reactContext);
}
@Override
public String getName() {
return "BGNativeModuleExample";
}
}
其中需要注意的是,模块名前的RCT前缀会被自动移除。例如”RCTBGNativeModuleExample”,在JS中的模块名字仍然是BGNativeModuleExample。
到这一步的时候,是不是就可以访问这个模块了?事实是现在还不能访问,因为我们还需要注册这个模块。
为了注册原生模块,我们首先创建一个BGNativeExamplePackage类,这个类要求实现ReactPackage接口,这个接口有三个方法createNativeModules、createJSModules和createViewManagers。其中,createNativeModules是用来添加原生模块的;createViewManagers是用来添加原生的UI组件;createJSModules暂时没用过,但是看官方的注释,应该是注册我们原生模块中用到的JS模块。
我们这里只需要在createNativeModules方法中添加原生模块,其他两个方法返回空数组就行了,整个package类的代码如下:
package
com.liuchungui.nativemoduleexample;
import
com.facebook.react.ReactPackage;
import
com.facebook.react.bridge.JavaScriptModule;
import
com.facebook.react.bridge.NativeModule;
import
com.facebook.react.bridge.ReactApplicationContext;
import
com.facebook.react.uimanager.ViewManager;
import
java.util.Arrays;
import
java.util.Collections;
import
java.util.List;
public
class BGNativeExamplePackage implements ReactPackage {
@Override
public
List createNativeModules(ReactApplicationContext reactContext)
{
return
Arrays.asList(new NativeModule[]{
new
BGNativeExampleModule(reactContext),
});
}
public
List> createJSModules() {
return
Collections.emptyList();
}
@Override
public
List createViewManagers(ReactApplicationContext reactContext)
{
return
Collections.emptyList();
}
}
最后,这个package需要在MainActivity.; border-radius: 5px; border-width: 0.1px;">android/app/src/main/java/com/testproject/MainActivity.java。
@Override
protected
List getPackages() {
return
Arrays.asList(
new
MainReactPackage(),
new
BGNativeExamplePackage()
);
}
到这个时候,我们终于可以在js中访问我们的原生模块了
import {
NativeModules } from 'react-native';
var
BGNativeModuleExample = NativeModules.BGNativeModuleExample;
console.log(BGNativeModuleExample);
2、为原生模块添加方法
要导出一个方法给JS使用,Java方法需要使用注解@ReactMethod。方法的返回类型必须为void。ReactNative跨语言访问是异步进行的,所以想要给JS返回一个值,唯一的办法是使用回调函数或者发送事件。
import
com.facebook.react.bridge.ReactMethod;
@ReactMethod
public
void testPrint(String name, ReadableMap info) {
Log.i(TAG, name);
Log.i(TAG, info.toString());
}
在JS中,我们如下调用这个方法:
BGNativeModuleExample.testPrint("Jack", {
height:
'1.78m',
weight:
'7kg'
});
3、参数类型
在编写原生模块的时候,我们还需要知道原生Java数据类型和JS数据类型的映射关系:
Boolean
-> Bool
Integer
-> Number
Double
-> Number
Float
-> Number
String
-> String
Callback
-> function
ReadableMap -> Object
ReadableArray -> Array
4、回调函数
原生模块还支持一种特殊的参数——回调函数。它提供了一个函数来把返回值传回给JS。
import
com.facebook.react.bridge.Callback;
@ReactMethod
public
void getNativeClass(Callback callback) {
callback.invoke("BGNativeExampleModule");
}
在JS中,我们可以通过以下方式获取到原生模块的类名:
1
2
3
BGNativeModuleExample.getNativeClass(name => {
console.log("nativeClass: ",
name);
});
5、Promises
原生模块还可以使用promise来简化代码,搭配ES2016(ES7)标准的async/await语法则效果更佳。如果桥接原生方法的最后一个参数是一个Promise,则对应的JS方法就会返回一个Promise对象。
1
2
3
4
5
6
7
8
9
10
import
com.facebook.react.bridge.Promise;
@ReactMethod
public void testPromises(Boolean isResolve,
Promise promise) {
if(isResolve) {
promise.resolve(isResolve.toString());
}
else
{
promise.reject(isResolve.toString());
}
}
JS中如下调用
1
2
3
4
5
6
7
BGNativeModuleExample.testPromises(true)
.then(result => {
console.log("result is ",
result);
})
.catch(result => {
console.log("result = ", result);
});
6、导出常量
我们在getContants方法中导出js所需要使用的常量。
1
2
3
4
5
6
7
@Override
public Map getConstants() {
final
Map constants = new HashMap<>();
constants.put("BGModuleName", "BGNativeModuleExample");
constants.put(TestEventName, TestEventName);
return constants;
}
我们在JS中打印BGModuleName这个常量的值:
1
console.log("BGModuleName const value =
", BGNativeModuleExample.BGModuleName);
7、给JS发送事件
原生模块可以在没有被调用的情况下往JS发送事件通知,最简单的办法是通过RCTDeviceEventEmitter,这可以通过ReactContext获得对应的引用。
在这里,我们为了能够接收到事件,开启了一个定时器,每一秒发送一次事件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class BGNativeExampleModule
extends ReactContextBaseJavaModule
{
protected static final String TAG
= BGNativeExampleModule.class.getSimpleName();
private static final String
TestEventName = "TestEventName";
private Timer timer;
public BGNativeExampleModule(final ReactApplicationContext
reactContext) {
super(reactContext);
//开启定时器
TimerTask
task = new TimerTask() {
@Override
public void run() {
//发送事件
WritableMap params = Arguments.createMap();
params.putString("name", "Jack");
reactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(TestEventName, params);
}
};
timer =
new
Timer();
timer.schedule(task, 1000, 1000);
}
}
在JS中,我们这里直接使用DeviceEventEmitter模块来监听事件。
1
2
3
4
//接收事件
DeviceEventEmitter.addListener(BGNativeModuleExample.TestEventName,
info => {
console.log(info);
});
官方文档中,说也可以使用Subscribable
mixin的addListenerOn方法来接收事件,其实它就是对DeviceEventEmitter一种封装。而且,在es6之后,不再支持mixins,所以我们直接使用DeviceEventEmitter接收事件比较好。
8、生命周期
有时候,为了某些目的,我们需要监听ReactNative应用的生命周期。我们可以让原生模块实现LifecycleEventListener接口,然后使用addLifecycleEventListener注册一下监听。
1
2
3
4
5
public BGNativeExampleModule(final ReactApplicationContext
reactContext) {
super(reactContext);
//添加监听
reactContext.addLifecycleEventListener(this);
}
实现LifecycleEventListener接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
public
void
onHostResume() {
Log.i(TAG, "onHostResume");
}
@Override
public
void
onHostPause() {
Log.i(TAG, "onHostPause");
timer.cancel();
}
@Override
public
void
onHostDestroy() {
Log.i(TAG, "onHostDestroy");
timer.cancel();
}
这样,我们就可以监听ReactNative应用的生命周期了。
上面原生代码就编写好了,主要以代码实践为主,弥补官方文档中的一些不足,如果要需要了解更多的原生模块封装的知识,可以参考原生模块,也可以参考官方的源代码。
二、发布上线
建立一个github仓库
在github上创建一个仓库react-native-BGNativeModuleExample,克隆到本地,并且创建一个android文件夹。
1
2
3
$ git
clone
https://github.com/liuchungui/react-native-BGNativeModuleExample.git
$
cd
react-native-BGNativeModuleExample
$ mkdir
android
将Library复制到android文件夹下
我们首先进入前面创建的nativemoduleexample这个Library下,然后将这个文件目录下所有文件copy到react-native-BGNativeModuleExample/android目录下。
$ cp -R
android/nativemoduleexample/*
~/github/react-native-BGNativeModuleExample/android
发布到npm
这里请参考我前面写的ReactNative之原生模块开发并发布——iOS篇中的发布到npm那一节的内容,当然如果你和我一样前面已经发布了1.0.0,只需要将package.json修改一个版本就好了,我这里修改为2.0.0,然后使用下面命令发布。
$ npm
publish
+
react-native-nativemodule-example@2.0.0
这样,我们就成功发布到了npmjs.org。
三、添加Example,测试是否可用,添加README
请点击查看ReactNative之原生模块开发并发布——iOS篇中的添加Example,测试是否可用,添加README这一部分内容,基本上一样的。
参考:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。