React Native的Android Native定制开发
使用RN开发APP,可能要做的某个功能只能用Native来实现,比如调用摄像头、打电话等,但是RN又没有提供相应的组件或模块, 这就需要我们去定制一个Native Module或Native Components。定制完成后可以放在git上,这样再遇到相同功能可以直接引用, 或者发布一个npm模块共享出来,也能帮助下别人。
开发工具采用Android Studio,首先创建一个Android Library,然后添加RN Dependency
不能同时拥有Module和Component两个身份,定义为Module的不能包含组件,反之同理。
定义Module类
public class ModuleName extends ReactContextBaseJavaModule { // 根据开发的功能来定义ModuleName
/**
* 根据开发的功能来定义JSModuleName
* JS端通过此名称来使用本Module
* 若前面有RCT,RN会自动忽略,如:RCTCardHelp,在JS端实际为CardHelp,建议一律不加RCT
* */
private final static String REACT_CLASS = "JSModuleName";
public ModuleName(ReactApplicationContext reactContext) {
super(reactContext);
}
@Override
public String getName() {
return REACT_CLASS;
}
}
在Module类里定义一个方法供JS调用
/**
* 根据方法的功能来定义methodName
* 类型必须为public void
* */
@ReactMethod
public void methodName(){
}
/**
* Callback通知JS结果
*
* result类型与JS类型的对应关系:
* Boolean -> Bool
* Integer -> Number
* Double -> Number
* Float -> Number
* String -> String
* Callback -> function
* ReadableMap -> Object
* ReadableArray -> Array
* */
@ReactMethod
public void methodName(Callback callback){
try{
callback.invoke(null,result);
} catch (Exception e){
callback.invoke(e);
}
}
/**
* Promise通知JS结果
* result同上
* */
@ReactMethod
public void methodName(Promise promise){
try{
promise.resolve(result);
} catch (Exception e) {
promise.reject("err",e);
}
}
如果程序需要调用第三方SDK,比如使用微信SDK的分享功能,就需要一个Events来通知分享的结果
/**
* reactContext为Module构造函数传入的参数
* eventName对应JS获取事件的名称
* params为返回结果,类型为object
* */
private void sendEvent(ReactContext reactContext,
String eventName,
@Nullable WritableMap params) {
reactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName, params);
}
有时候需要启动一个Native界面,比如调用相机拍照
public class ModuleName extends ReactContextBaseJavaModule implements ActivityEventListener {
private final static int REQUEST_CODE = 0x1001;
private Callback mCallback;
private Promise mPromise;
public ModuleName(ReactApplicationContext reactContext) {
super(reactContext);
reactContext.addActivityEventListener(this);
}
/**
* 以Callback为例,Promise同理
* */
@ReactMethod
public void methodName(Callback callback){
Activity currentActivity = getCurrentActivity();
if (currentActivity == null) {
callback.invoke("Activity doesn't exist");
return;
}
this.mCallback = callback;
try {
currentActivity.startActivityForResult(
new Intent(
getReactApplicationContext().getBaseContext()
, TargetActivity.class), REQUEST_CODE);
} catch (Exception e) {
mCallback.invoke(e);
mCallback = null;
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK && requestCode == REQUEST_CODE) {
if (mCallback != null) {
if (data != null) {
String result = data.getStringExtra("result");
mCallback.invoke(result);
} else {
mCallback.invoke("no data found");
}
mCallback = null;
}
}
}
}
如果需要监听程序的生命周期,RN提供了如下方式
public class ModuleName extends ReactContextBaseJavaModule implements LifecycleEventListener {
public ModuleName(ReactApplicationContext reactContext) {
super(reactContext);
reactContext.addLifecycleEventListener(this);
}
@Override
public void onHostResume() {
// Actvity onResume
}
@Override
public void onHostPause() {
// Actvity onPause
}
@Override
public void onHostDestroy() {
// Actvity onDestroy
}
}
创建Package
public class PackageName implements ReactPackage{ // 根据开发的功能来定义PackageName
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
return Arrays.<NativeModule>asList(new ModuleName(reactContext)); //在此处把定义的Module加进来
}
@Override
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
创建index.js
var React = require('react-native');
var { NativeModules } = React;
module.exports = NativeModules.JSModuleName; // 对应定义在Module里的REACT_CLASS
创建package.json
推荐采用命令行来创建package.json
$ npm init
定义Components类
/**
* 根据开发的组件来定义ViewNameManager
* YouView为你开发的组件,可以是Android提供的View,也可以是自定义的View
* */
public class ViewNameManager extends SimpleViewManager<YouView> {
/**
* 根据开发的组件来定义RCTViewName
* JS端通过此名称来使用本组件
* RCT不会被忽略,名称与JS端一致
* */
private static final String REACT_CLASS = "RCTViewName";
private static final int COMMAND_METHOD_ONE = 1;
private static final int COMMAND_METHOD_TWO = 2;
@Override
protected YouView createViewInstance(ThemedReactContext reactContext) {
return new YouView(reactContext);
}
@Override
public String getName() {
return REACT_CLASS;
}
/**
* @ReactProp用来定义组件的属性
* 支持下列属性类型:
* String
* boolean
* float
* int
* ReadableArray
* ReadableMap(应该也支持,未验证)
* */
@ReactProp(name = "isLoop",defaultBoolean = true)
public void isLoop(YouView view, boolean isLoop){
view.isLoop(isLoop);
}
/**
* 给JS提供methodOne()和methodTwo()两个方法
* 可以是一个或更多个方法,具体方法名和数量根据需要定义
* */
@Override
public Map<String,Integer> getCommandsMap() {
return MapBuilder.of(
"methodOne",
COMMAND_METHOD_ONE,
"methodTwo",
COMMAND_METHOD_TWO);
}
/**
* 当JS调用methodOne()或methodTwo()时,本地方法的相应实现
* */
@Override
public void receiveCommand(YouView view, int commandId, ReadableArray args) {
switch (commandId) {
case COMMAND_METHOD_ONE: {
view.previous();
return;
}
case COMMAND_METHOD_TWO: {
view.next();
return;
}
default:
throw new IllegalArgumentException(String.format(
"Unsupported command %d received by %s.",
commandId,
getClass().getSimpleName()));
}
}
}
View的Event
class YouView extends View {
...
public void onReceiveNativeEvent() {
WritableMap event = Arguments.createMap();
event.putString("message", "MyMessage");
ReactContext reactContext = (ReactContext)getContext();
reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(
getId(),
"topChange",
event);
}
}
创建Package
public class PackageName implements ReactPackage{ // 根据开发的功能来定义PackageName
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
@Override
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Arrays.<ViewManager>asList(new ViewNameManager()); //在此处把定义的ViewManager加进来
}
}
创建index.js
import React,{
Component,
PropTypes,
View
} from 'react';
import ReactNative,{
requireNativeComponent,
NativeModules
} from 'react-native';
let UIManager = NativeModules.UIManager;
let NativeYouView = requireNativeComponent('RCTViewName',YouView);
export default class YouView extends React.Component{
static propTypes = {
isLoop: PropTypes.bool,
};
methodOne() {
UIManager.dispatchViewManagerCommand(
ReactNative.findNodeHandle(this),
UIManager.RCTViewName.Commands.methodOne, // 调用定义在ViewManager里的methodOne
null,
);
};
methodTwo(){
UIManager.dispatchViewManagerCommand(
ReactNative.findNodeHandle(this),
UIManager.RCTViewName.Commands.methodTwo, // 调用定义在ViewManager里的methodTwo
null,
);
}
render(){
return <NativeYouView {...this.props} />;
}
};
创建package.json
推荐采用命令行来创建package.json
$ npm init
创建Android Library
File -> New -> New Module -> Android Library -> give name
添加RN依赖
File -> Project Structure -> you module -> Dependencies -> add Library Dependency