当有时候RN项目需要访问原生的api但是rn官方并还没封装这个模块时,就需要使用自己去手动封装。调用Android原生代码,以最简单的弹Toast为例:
-
定义ReactContextBaseJavaModule模块
- 首先在android的目录下定义一个类去继承ReactContextBaseJavaModule类,实现getName方法。此方法返回的参数就是你使用js调用的模块名。
- 根据自己的需求选择是否去重写getConstants方法,这个方法的作用是给js提供一些常量使用,通过map的键值去存储,map的键是js调用的名称,key是对应的值。
- 然后就是定义你对js提供的方法,对js提供的方法必须是没有返回值的,如果需要有返回值的话,则需要增加Callback参数,js通过callback获取函数需要返回的值。需要注意的是必须在方法上面加上@ReactMethod注解,才能让js识别调用。具体代码如下:
/**
* 原生模块
*/
public class ToastModule extends ReactContextBaseJavaModule {
private static final String DURATION_SHORT_KEY = "SHORT";
private static final String DURATION_LONG_KEY = "LONG";
public ToastModule(ReactApplicationContext reactContext) {
super(reactContext);
}
/**
*
* @return js调用的模块名
*/
@Override
public String getName() {
return "ToastModule";
}
/**
* 给rn定义模块的一些常量
* @return 常量的一些键值
*/
@Nullable
@Override
public Map<String, Object> getConstants() {
final Map<String, Object> constants = new HashMap<>();
constants.put(DURATION_SHORT_KEY, Toast.LENGTH_SHORT);
constants.put(DURATION_LONG_KEY, Toast.LENGTH_LONG);
return constants;
}
/**
* 使用ReactMethod注解,使这个方法被js调用
* @param message 文本
* @param duration 时长
*/
@ReactMethod
public void show(String message, int duration, Callback success,Callback error) {
try {
Toast.makeText(getReactApplicationContext(), message, duration).show();
success.invoke("success");
}
catch (Exception e){
error.invoke("error");
}
}
}
- 定义好模块之后,接下来就需要去注册模块。非常简单,定义一个类去实现,ReactPackage接口。这个接口只有两个方法,createViewManagers他是注册需要被调用原生控件的(下篇文章会讲到),createNativeModules就是注册原生方法需要实现的,他的返回值是个list,创建一个list然后把你之前定义的ReactContextBaseJavaModule添加进去就好。代码如下。
public class ToastReactPackage implements ReactPackage {
/**
*
* @param reactContext 上下文
* @return 需要调用的原生控件
*/
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
/**
*
* @param reactContext 上下文
* @return 需要调用的原生模块
*/
@Override
public List<NativeModule> createNativeModules(
ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new ToastModule(reactContext));
return modules;
}
}
- 把上一步定义好的ReactPackage模块在application下注册。原生这边所有的工作就已经结束。
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new ToastReactPackage()//新添加需要注册的模块
);
}
};
- 原生端工作已完成。接下来就是js端,只需要加载NativeModules模块,
从NativeModules中获取之前定义的ReactContextBaseJavaModule对应的getName的值就可以调用对应的方法了,具体如下:
import React, {Component} from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
NativeModules
} from 'react-native';
export default class RNDemo extends Component {
_toast(){
NativeModules.ToastModule.show('toast', NativeModules.ToastModule.SHORT,(success)=>{alert(success)},(error)=>{alert(error)})
}
render() {
return (
<View style={styles.container}>
<Text onPress={this._toast} style={styles.welcome}>
Welcome to React Native!
</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
});
AppRegistry.registerComponent('RNDemo', () => RNDemo);