1,为什么需要RN与原生互调?
RN会有完成不了的事情,比如打开本地的相机,native接口。跟硬件打交道的话就不行了。
2,首先使用Android studio打开ReactNative项目下的android目录;
我们观察这个项目:它有两个类,MainActivity 和MainApplication;在app.gradle文件中,dependencies(依赖)中,会发现它有一个react-native的依赖包。
3,js调用java,是js先调用c,c在调用java。
4,js调用java流程
创建ToastExampleMoudle
package com.nativeapp;
import android.widget.Toast;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nullable;
//
public class ToastExampleMoudle extends ReactContextBaseJavaModule {
private static final String MESSAGE = "MESSAGE";
public ToastExampleMoudle(ReactApplicationContext reactContext) {
super(reactContext);
}
//通过js去弹出一个原生的吐丝
@ReactMethod//要想这个方法暴露给js必须增加这个注解
public void show(int duration){
Toast.makeText(getReactApplicationContext(), "dongnao:" + duration, Toast.LENGTH_SHORT).show();
}
@Override
public String getName() {
return "ToastForAndroid";
}
@Override
public boolean canOverrideExistingModule() {
return true;
}
}
创建ExampleReactNativePackage类
package com.nativeapp;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
//注册模块,注册到c
public class ExampleReactNativePackage implements ReactPackage {
//完成注入,需要被js调用java方法
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
//NativeModule添加到这个集合里,React Native通过这个方法知道当前有多少个需要给js调用的对象
List<NativeModule> modules = new ArrayList<>();
modules.add(new ToastExampleMoudle(reactContext));
return modules;
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
//注意这里不能写return null;
return Collections.emptyList();
}
}
这是MainActivity
package com.nativeapp;
import com.facebook.react.ReactActivity;
public class MainActivity extends ReactActivity {
/**
* Returns the name of the main component registered from JavaScript.
* This is used to schedule rendering of the component.
*/
//这里的nativeapp与index.js中的appName名字一样
//AppRegistry.registerComponent(appName, () => App);
@Override
protected String getMainComponentName() {
return "nativeapp";
}
}
修改MainApplication
package com.nativeapp;
import android.app.Application;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.soloader.SoLoader;
import java.util.Arrays;
import java.util.List;
//注册java的模块,提前应用到缓存中心
public class MainApplication extends Application implements ReactApplication {
// reactNative主机
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(),
//添加我们定义的ReactPackage
new ExampleReactNativePackage()
);
}
@Override
protected String getJSMainModuleName() {
//指向了程序的入口,index就是rn的主界面
return "index";
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
SoLoader.init(this, /* native exopackage */ false);
}
}
看下App.js文件
/**
* Sample React Native App
* https://github.com/facebook/react-native
*
* @format
* @flow
* @lint-ignore-every XPLATJSCOPYRIGHT1
*/
import React, {Component} from 'react';
//这里的ToastAndroid,是ToastExampleMoudle类中方法
// @Override
// public String getName() {
// return "ToastForAndroid";
// }
// 返回的名字。
import {
Platform,
StyleSheet,
ToastAndroid,
DeviceEventEmitter,
Text,
TouchableOpacity,
View,
NativeModules
} from 'react-native';
export default class App extends Component<Props> {
_onPressButton() {
/*调用ToastExampleMoudle类这个方法
* @ReactMethod
* public void show(int duration){
* Toast.makeText(getReactApplicationContext(), "dongnao:" + duration, Toast.LENGTH_SHORT).show();
* }
*/
// NativeModules.ToastForAndroid就表示调用ToastExampleMoudle类。
NativeModules.ToastForAndroid.show(1000);
}
render() {
return (
<View style={styles.container}>
//this._onPressButton.bind(this),调用_onPressButton(),注意这里是es6写法
<TouchableOpacity onPress={this._onPressButton.bind(this)}>
<Text style={styles.hello}>点击了</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
hello: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
我们运行项目,点击文字“点击了”,就会弹出吐丝
5,
ToastExampleMoudle类里面增加如下代码
//1,具有回调参数的
//2,callback是ReactNative里面的一个接口。这个接口只有一个方法。
@ReactMethod
public void testAndroidCallBack(String msg, Callback callback) {
Toast.makeText(getReactApplicationContext(),msg,Toast.LENGTH_LONG).show();
//invoke方法的参数是一个可变参数
callback.invoke("david");
}
修改app.js类
/**
* Sample React Native App
* https://github.com/facebook/react-native
*
* @format
* @flow
* @lint-ignore-every XPLATJSCOPYRIGHT1
*/
import React, {Component} from 'react';
//这里的ToastAndroid,是ToastExampleMoudle类中方法
// @Override
// public String getName() {
// return "ToastForAndroid";
// }
// 返回的名字。
import {
Platform,
StyleSheet,
ToastAndroid,
DeviceEventEmitter,
Text,
TouchableOpacity,
View,
NativeModules
} from 'react-native';
export default class App extends Component<Props> {
constructor(props) {
super(props);
this.state = {
text: "river",
text2: "默认",
}
}
_onPressButton2() {
NativeModules.ToastForAndroid.testAndroidCallBack("hello David", (result) => {
this.setState({
text: result
})
});
}
render() {
return (
<View style={styles.container}>
<TouchableOpacity onPress={this._onPressButton2.bind(this)}>
<Text style={styles.hello}>{this.state.text}</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
hello: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
运行项目,界面出现river文字,点击文字river,文字river变成david;同时弹出吐丝
“hello David”
6,
不适用回调方法传递参数,通过promise实现。
在ToastExampleMoudle里添加方法
//最后一个参数是promise
@ReactMethod
public void textAndroidPromiseMethod(String msg, Promise promise){
Toast.makeText(getReactApplicationContext(),msg,Toast.LENGTH_SHORT).show();
String result = "david";
//resolve代表正常运行,会走下面的then方法,如果是reject,则会走catch();
promise.resolve(result);
}
修改下app.js文件
/**
* Sample React Native App
* https://github.com/facebook/react-native
*
* @format
* @flow
* @lint-ignore-every XPLATJSCOPYRIGHT1
*/
import React, {Component} from 'react';
//这里的ToastAndroid,是ToastExampleMoudle类中方法
// @Override
// public String getName() {
// return "ToastForAndroid";
// }
// 返回的名字。
import {
Platform,
StyleSheet,
ToastAndroid,
DeviceEventEmitter,
Text,
TouchableOpacity,
View,
NativeModules
} from 'react-native';
export default class App extends Component<Props> {
constructor(props) {
super(props);
this.state = {
text: "river",
text2: "默认",
}
}
_onPressButton3() {
NativeModules.ToastForAndroid.textAndroidPromiseMethod("hello David").then((result) => {
this.setState({
text2: result
})
})
}
render() {
return (
<View style={styles.container}>
<TouchableOpacity onPress={this._onPressButton3.bind(this)}>
<Text style={styles.hello}>{this.state.text2}</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
hello: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
运行效果,屏幕出现"默认"二字,点击这两个字则默认变为david,同时弹出吐丝"hello david"
7,给js传递一个对象。我们不使用callback,也不使用promise,通过另外一种方式。
完成下面的操作
js作为一个被观察者,想去通知观察者, 比如js有生命周期,我想通知java,生命周期到了一个什么程度。java里面有一方法,但这个方法并不是现在调用,当react某个页面加载的时候,我在通知java这个方法进行调用
我们在ToastExampleMoudle里添加代码
private static final String MESSAGE = "MESSAGE";
@Nullable
@Override
public Map<String, Object> getConstants() {
//让js那边能够使用这些常量
Map<String,Object> constants = new HashMap<>();
constants.put(MESSAGE,"动脑 常量");
return constants;
}
@ReactMethod
public void onScaning() {
WritableMap params = Arguments.createMap();
params.putString("key","mydata");
//通过java的方式发送一个对象事件,java通知js
getReactApplicationContext().getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit("EventName", params);
}
看下App.js文件
/**
* Sample React Native App
* https://github.com/facebook/react-native
*
* @format
* @flow
* @lint-ignore-every XPLATJSCOPYRIGHT1
*/
import React, {Component} from 'react';
//这里的ToastAndroid,是ToastExampleMoudle类中方法
// @Override
// public String getName() {
// return "ToastForAndroid";
// }
// 返回的名字。
import {
Platform,
StyleSheet,
ToastAndroid,
DeviceEventEmitter,
Text,
TouchableOpacity,
View,
NativeModules
} from 'react-native';
export default class App extends Component<Props> {
constructor(props) {
super(props);
this.state = {
text: "river",
text2: "默认",
}
}
componentDidMount(): void {
//1,观察者对象 监听java 调用js
//2,js去监听java
//3.msg为我接收的数据
DeviceEventEmitter.addListener("EventName", function (msg) {
//获取到类里面的成员变量:
let rest = NativeModules.ToastForAndroid.MESSAGE;
//这个ToastAndroid是ReactNative自己带的。
ToastAndroid.show("DeviceEventEmitter收到消息:" + "\n" + rest, ToastAndroid.SHORT)
})
}
//js作为一个被观察者,想去通知观察者,
// 比如js有生命周期,我想通知java,生命周期到了一个什么程度。
//java里面有一方法,但这个方法并不是现在调用,当react某个页面
//加载的时候,我在通知java这个方法进行调用
_onPressButton4() {
NativeModules.ToastForAndroid.onScaning();
}
render() {
return (
<View style={styles.container}>
<TouchableOpacity onPress={this._onPressButton4.bind(this)}>
<Text style={styles.hello}>{this.state.text2}</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
hello: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
我们点击文字,触发_onPressButton4方法,然后会触发android中的onScaning方法;调用onScanning方法的时候执行下面代码会生成一个对象WritableMap params = Arguments.createMap(); params.putString("key","mydata");
然后,我们通过getReactApplicationContext().getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) .emit("EventName", params);
发送了一个名字叫EventName的事件,
最终运行效果:我们运行项目,屏幕上出现"默认"文字,点击"默认"弹出:“DeviceEventEmitter收到消息:动脑常量”
8,我把完整的代码贴上:
package com.nativeapp;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
//注册模块,注册到c
public class ExampleReactNativePackage implements ReactPackage {
//完成注入,需要被js调用java方法
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
//NativeModule添加到这个集合里,React Native通过这个方法知道当前有多少个需要给js调用的对象
List<NativeModule> modules = new ArrayList<>();
modules.add(new ToastExampleMoudle(reactContext));
return modules;
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
//注意这里不能写return null;
return Collections.emptyList();
}
}
package com.nativeapp;
import com.facebook.react.ReactActivity;
public class MainActivity extends ReactActivity {
/**
* Returns the name of the main component registered from JavaScript.
* This is used to schedule rendering of the component.
*/
@Override
protected String getMainComponentName() {
return "nativeapp";
}
}
package com.nativeapp;
import android.widget.Toast;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nullable;
//
public class ToastExampleMoudle extends ReactContextBaseJavaModule {
private static final String MESSAGE = "MESSAGE";
public ToastExampleMoudle(ReactApplicationContext reactContext) {
super(reactContext);
}
@Nullable
@Override
public Map<String, Object> getConstants() {
//让js那边能够使用这些常量
Map<String,Object> constants = new HashMap<>();
constants.put(MESSAGE,"动脑 常量");
return constants;
}
//通过js去弹出一个原生的吐丝
@ReactMethod//要想这个方法暴露给js必须增加这个注解
public void show(int duration){
Toast.makeText(getReactApplicationContext(), "dongnao:" + duration, Toast.LENGTH_SHORT).show();
}
//1,具有回调参数的
//2,callback是ReactNative里面的一个接口。这个接口只有一个方法。
@ReactMethod
public void testAndroidCallBack(String msg, Callback callback) {
Toast.makeText(getReactApplicationContext(),msg,Toast.LENGTH_LONG).show();
//invoke方法的参数是一个可变参数
callback.invoke("david");
}
//最后一个参数是promise
@ReactMethod
public void textAndroidPromiseMethod(String msg, Promise promise){
Toast.makeText(getReactApplicationContext(),msg,Toast.LENGTH_SHORT).show();
String result = "david";
//resolve代表正常运行,会走下面的then方法,如果是reject,则会走catch();
promise.resolve(result);
}
@ReactMethod
public void onScaning() {
WritableMap params = Arguments.createMap();
params.putString("key","mydata");
//通过java的方式发送一个对象事件,java通知js
getReactApplicationContext().getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit("EventName", params);
}
@Override
public String getName() {
return "ToastForAndroid";
}
@Override
public boolean canOverrideExistingModule() {
return true;
}
}
package com.nativeapp;
import android.app.Application;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.soloader.SoLoader;
import java.util.Arrays;
import java.util.List;
//注册java的模块,提前应用到缓存中心
public class MainApplication extends Application implements ReactApplication {
// reactNative主机
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(),
//添加我们定义的ReactPackage
new ExampleReactNativePackage()
);
}
@Override
protected String getJSMainModuleName() {
//指向了程序的入口,index就是rn的主界面
return "index";
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
SoLoader.init(this, /* native exopackage */ false);
}
}
/**
* Sample React Native App
* https://github.com/facebook/react-native
*
* @format
* @flow
* @lint-ignore-every XPLATJSCOPYRIGHT1
*/
import React, {Component} from 'react';
//这里的ToastAndroid,是ToastExampleMoudle类中方法
// @Override
// public String getName() {
// return "ToastForAndroid";
// }
// 返回的名字。
import {
Platform,
StyleSheet,
ToastAndroid,
DeviceEventEmitter,
Text,
TouchableOpacity,
View,
NativeModules
} from 'react-native';
export default class App extends Component<Props> {
constructor(props) {
super(props);
this.state = {
text: "river",
text2: "默认",
}
}
componentDidMount(): void {
//1,观察者对象 监听java 调用js
//2,js去监听java
//3.msg为我接收的数据
DeviceEventEmitter.addListener("EventName", function (msg) {
//获取到类里面的成员变量:
let rest = NativeModules.ToastForAndroid.MESSAGE;
//这个ToastAndroid是ReactNative自己带的。
ToastAndroid.show("DeviceEventEmitter收到消息:" + "\n" + rest, ToastAndroid.SHORT)
})
}
_onPressButton() {
/*调用ToastExampleMoudle类这个方法
* @ReactMethod
* public void show(int duration){
* Toast.makeText(getReactApplicationContext(), "dongnao:" + duration, Toast.LENGTH_SHORT).show();
* }
*/
NativeModules.ToastForAndroid.show(1000);
}
_onPressButton2() {
NativeModules.ToastForAndroid.testAndroidCallBack("hello David", (result) => {
this.setState({
text: result
})
});
}
_onPressButton3() {
NativeModules.ToastForAndroid.textAndroidPromiseMethod("hello David").then((result) => {
this.setState({
text2: result
})
})
}
//js作为一个被观察者,想去通知观察者,
// 比如js有生命周期,我想通知java,生命周期到了一个什么程度。
//java里面有一方法,但这个方法并不是现在调用,当react某个页面
//加载的时候,我在通知java这个方法进行调用
_onPressButton4() {
NativeModules.ToastForAndroid.onScaning();
}
render() {
return (
<View style={styles.container}>
//this._onPressButton.bind(this),调用_onPressButton(),注意这里是es6写法
<TouchableOpacity onPress={this._onPressButton.bind(this)}>
<Text style={styles.hello}>点击了</Text>
</TouchableOpacity>
<TouchableOpacity onPress={this._onPressButton2.bind(this)}>
<Text style={styles.hello}>{this.state.text}</Text>
</TouchableOpacity>
<TouchableOpacity onPress={this._onPressButton3.bind(this)}>
<Text style={styles.hello}>{this.state.text2}</Text>
</TouchableOpacity>
<TouchableOpacity onPress={this._onPressButton4.bind(this)}>
<Text style={styles.hello}>{this.state.text2}</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
hello: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});