React Native学习之RN与原生通信
React Native与原生通信,本文适配Android原生与RN的混合开发
实例一:页面跳转
RN触发Android原生的方法:(启动一个新的原生界面Activity)界面切换
开启意图一定要添加语句:
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
MyNativeModule.java
public class MyNativeModule extends ReactContextBaseJavaModule {
private ReactApplicationContext context;
public MyNativeModule(ReactApplicationContext reactContext) {
super(reactContext);
context = reactContext;
}
@Override
public String getName() {
//一定要返回一个名字,在RN代码里面需要使用这个名字来调用该类的方法
return "MyNativeModule";
}
//函数不能有返回值,因为被调用的原生代码是异步的,原生代码执行结束之后只能通过回调函数或者发送消息给RN
//必须声明ReactMethod注解
@ReactMethod
public void rnCallNative(String msg) {
Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
Intent intent = new Intent(context, MyActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); //必须添加这条语句,否则报错
context.startActivity(intent);
}
}
MyActivity.java
public class MyActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
}
public void onBack(View v) {
finish();
}
}
activity_my.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:android="http://schemas.android.com/apk/res/android"
>
<Button
android:text="原生按钮 - 点击退出"
android:onClick="onBack"
android:layout_centerInParent="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#3BC1FF"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:textColor="#FFFFFF"
/>
</RelativeLayout>
index.android.js
/**
* Sample React Native App
* https://github.com/facebook/react-native
* @flow
*/
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
NativeModules
} from 'react-native';
class RNAPP extends Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
Welcome to React Native!
</Text>
<Text style={styles.welcome}>
React Native混合原生开发
</Text>
<Text style={styles.instructions}>
Powered by rnapp.cc
</Text>
<Text onPress={this.callNative.bind(this)} style={styles.btn}>页面跳转</Text>
</View>
);
}
callNative() {
NativeModules.MyNativeModule.rnCallNative('React Native 调用原生模块的方法\nPowered by RNAPP.CC');
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
btn: {
marginTop: 50,
marginLeft: 10,
marginRight: 10,
width: 200,
height: 35,
backgroundColor: '#3BC1FF',
color: '#fff',
lineHeight: 24,
fontWeight: 'bold',
textAlign: 'center',
textAlignVertical:'center'
},
});
AppRegistry.registerComponent('RNAPP', () => RNAPP);
实例二:数据传递
实例:RN调用原生的界面,用户操作原生界面后将结果发送给RN侧(从RN侧启动Android原生的选择联系人界面,用户选择后,将其电话号码发送给RN侧)
注意在 AndroidManifest.xml 中添加读写通讯录权限
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
- 启动选择联系人
Intent intent = new Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI);
Bundle bundle = new Bundle();
context.startActivityForResult(intent, REQUEST_CODE, bundle);
- 处理选择联系人后的结果(onActivityResult)
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode!= MyNativeModule.REQUEST_CODE || resultCode!=RESULT_OK)
return;
Uri contactUri = data.getData();
Cursor cursor = managedQuery(contactUri, null, null, null, null);
cursor.moveToFirst();
String phoneNumber = getContactPhone(cursor);
//将phoneNumber发送给RN侧,需要调用MyNativeModule对象里面的方法
MainApplication.getMyReactPackage().getMyNativeModule().sendMsgToRn(phoneNumber);
}
- 发送消息给RN侧
public void sendMsgToRn(String msg) {
//将消息msg发送给RN侧
context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("AndroidToRNMessage", msg);
}
源码:
MyNativeModule.java
public class MyNativeModule extends ReactContextBaseJavaModule {
private ReactApplicationContext context;
public static final int REQUEST_CODE = 1;
public MyNativeModule(ReactApplicationContext reactContext) {
super(reactContext);
context = reactContext;
}
@Override
public String getName() {
//一定要返回一个名字,在RN代码里面需要使用这个名字来调用该类的方法
return "MyNativeModule";
}
//函数不能有返回值,因为被调用的原生代码是异步的,原生代码执行结束之后只能通过回调函数或者发送消息给RN
//必须声明ReactMethod注解
@ReactMethod
public void showNativeMsg(String msg) {
Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
}
//必须声明ReactMethod注解
@ReactMethod
public void newNativePage() {
/*
Intent intent = new Intent(context, MyActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); //必须添加这条语句,否则报错
context.startActivity(intent);
*/
Intent intent = new Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI);
Bundle bundle = new Bundle();
context.startActivityForResult(intent, REQUEST_CODE, bundle);
}
public void sendMsgToRn(String msg) {
//将消息msg发送给RN侧
context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("AndroidToRNMessage", msg);
}
}
MainActivity.java
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 "AwesomeProject";
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode!= MyNativeModule.REQUEST_CODE || resultCode!=RESULT_OK)
return;
Uri contactUri = data.getData();
Cursor cursor = managedQuery(contactUri, null, null, null, null);
cursor.moveToFirst();
String phoneNumber = getContactPhone(cursor);
//将phoneNumber发送给RN侧,需要调用MyNativeModule对象里面的方法
MainApplication.getMyReactPackage().getMyNativeModule().sendMsgToRn(phoneNumber);
}
private String getContactPhone(Cursor cursor) {
int phoneColumn = cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER);
int phoneNum = cursor.getInt(phoneColumn);
String result = "";
if (phoneNum > 0) {
// 获得联系人的ID号
int idColumn = cursor.getColumnIndex(ContactsContract.Contacts._ID);
String contactId = cursor.getString(idColumn);
// 获得联系人电话的cursor
Cursor phone = getContentResolver().query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + "=" + contactId, null, null);
if (phone.moveToFirst()) {
for (; !phone.isAfterLast(); phone.moveToNext()) {
int index = phone.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
int typeindex = phone.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE);
int phoneType = phone.getInt(typeindex);
String phoneNumber = phone.getString(index);
result = phoneNumber;
/*
switch (phoneType) {
case 2:
result = phoneNumber;
break;
default:
break;
}
*/
}
if (!phone.isClosed()) {
phone.close();
}
}
}
return result;
}
}
MainApplication.java
public class MainApplication extends Application implements ReactApplication {
private static final MyReactPackage myReactPackage = new MyReactPackage();
public static MyReactPackage getMyReactPackage() {
return myReactPackage;
}
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
protected boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(), myReactPackage
);
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
}
MyReactPackage.java
public class MyReactPackage implements ReactPackage {
private MyNativeModule myNativeModule;
public MyNativeModule getMyNativeModule() {
return myNativeModule;
}
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
myNativeModule = new MyNativeModule(reactContext);
modules.add(myNativeModule);
return modules;
}
@Override
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
index.android.js
/**
* Sample React Native App
* https://github.com/facebook/react-native
* @flow
*/
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
NativeModules,
DeviceEventEmitter
} from 'react-native';
class RNAPP extends Component {
componentWillMount(){
DeviceEventEmitter.addListener('AndroidToRNMessage', this.handleAndroidMessage);
}
componentWillunMount() {
DeviceEventEmitter.remove('AndroidToRNMessage', this.handleAndroidMessage);
}
handleAndroidMessage = (msg) => {
console.log(msg);
NativeModules.MyNativeModule.showNativeMsg('手机号码是:' + msg + '\nPowered by RNAPP.CC');
}
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
Welcome to React Native!
</Text>
<Text style={styles.welcome}>
React Native混合原生开发
</Text>
<Text style={styles.instructions}>
Powered by rnapp.cc
</Text>
<Text onPress={this.newNativePage.bind(this)} style={styles.btn}>调用通讯录</Text>
</View>
);
}
newNativePage() {
NativeModules.MyNativeModule.newNativePage();
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
btn: {
marginTop: 50,
marginLeft: 10,
marginRight: 10,
width: 200,
height: 35,
backgroundColor: '#3BC1FF',
color: '#fff',
lineHeight: 24,
fontWeight: 'bold',
textAlign: 'center',
textAlignVertical:'center'
},
});
AppRegistry.registerComponent('RNAPP', () => RNAPP);
React Native混合原生开发之RN与原生通信扩展知识点
回调函数
import com.facebook.react.bridge.Callback;
缺点:
- 只应当调用一次,多次调用会有不可预期的结果
- Android侧无法主动通过回调函数向RN侧发送消息
推荐使用消息机制而不是回调函数,另外在RN侧对应的回调函数不会立即执行,因为桥接机制是异步的。
@ReactMethod
public void rnCallNativeCallback(Callback errorCallback, Callback successCallback){
try {
successCallback.invoke(100, 100, 200, 200); //调用回调函数,返回结果
} catch (IllegalViewOperationException e) {
errorCallback.invoke(e.getMessage());
}
}
Promise机制
import com.facebook.react.bridge.Promise;
如果被桥接的原生方法的最后一个参数是一个Promise对象,那么该方法会返回一个JS的Promise对象给与它对应的js方法。
@ReactMethod
public void rnCallNativePromise(String msg, Promise promise) {
try {
//业务逻辑处理
Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
String componentName = getCurrentActivity().getComponentName().toString();
promise.resolve(componentName);
}catch (Exception e){
promise.reject("100", e.getMessage()); //Promise 失败
}
}
跨语言常量
为了保持Android侧与RN侧常量的一致性,可以将Android原生代码的常量暴露给RN侧。在MyNativeModule类中复写getConstants(),在RN侧使用NativeModules.MyNativeModule.author
@Nullable
@Override
public Map<String, Object> getConstants() {
final Map<String, Object> constants = new HashMap<>();
constants.put("URL", "http://www.appblog.cn/");
constants.put("port", "8081");
constants.put("ip", "192.168.1.100");
constants.put("author", "AppBlog.CN");
return constants;
}
<Text style={styles.instructions}>
URL: {NativeModules.MyNativeModule.URL}
</Text>
<Text style={styles.instructions}>
Author: {NativeModules.MyNativeModule.author}
</Text>
多线程机制
如果原生代码模块执行需要较长的时间,应当自己启动一个线程并在线程中执行!最后通过回调方法进行获取执行结果即可。
Android中UI等待时间过长会报ANR异常。
监听ActivityResult与Android生命周期事件
implements ActivityEventListener, LifecycleEventListener
//增加监听,让当前类的onActivityResult处理返回
context.addActivityEventListener(this);
//在构造函数中注册生命周期事件的监听接口
context.addLifecycleEventListener(this);
如果我们需要监听Activity的生命周期,例如nResume、onPause等等方法,如果要实现这样的功能,那么当前模块类需要实现LifcycleEventListener接口并且在该模块类的构造函数中进行注册该接口。
ActivityEventListener 接口方法
void onActivityResult(int requestCode, int resultCode, Intent data)
void onNewIntent(Intent intent)
LifecycleEventListener 接口方法
void onHostResume()
void onHostPause()
void onHostDestroy()
初始启动的Activity设定
在AndroidManifest.xml文件中 带intent-filter
的Activity 可以设定开始启动的activity是RN的还是原生的
源码
MyNativeModule.java
public class MyNativeModule extends ReactContextBaseJavaModule implements ActivityEventListener, LifecycleEventListener {
private ReactApplicationContext context;
public static final int REQUEST_CODE = 1;
public MyNativeModule(ReactApplicationContext reactContext) {
super(reactContext);
context = reactContext;
//增加监听,让当前类的onActivityResult处理返回
context.addActivityEventListener(this);
//在构造函数中注册生命周期事件的监听接口
context.addLifecycleEventListener(this);
}
@Override
public String getName() {
//一定要返回一个名字,在RN代码里面需要使用这个名字来调用该类的方法
return "MyNativeModule";
}
//函数不能有返回值,因为被调用的原生代码是异步的,原生代码执行结束之后只能通过回调函数或者发送消息给RN
//必须声明ReactMethod注解
@ReactMethod
public void showNativeMsg(String msg) {
Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
}
//必须声明ReactMethod注解
@ReactMethod
public void rnCallNative() {
/*
Intent intent = new Intent(context, MyActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); //必须添加这条语句,否则报错
context.startActivity(intent);
*/
Intent intent = new Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI);
Bundle bundle = new Bundle();
context.startActivityForResult(intent, REQUEST_CODE, bundle);
}
public void sendMsgToRn(String msg) {
//将消息msg发送给RN侧
context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("AndroidToRNMessage", msg);
}
@ReactMethod
public void rnCallNativeCallback(Callback errorCallback, Callback successCallback){
try {
successCallback.invoke(100, 100, 200, 200); //调用回调函数,返回结果
} catch (IllegalViewOperationException e) {
errorCallback.invoke(e.getMessage());
}
}
@ReactMethod
public void rnCallNativePromise(String msg, Promise promise) {
try {
//业务逻辑处理
Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
String componentName = getCurrentActivity().getComponentName().toString();
promise.resolve(componentName);
}catch (Exception e){
promise.reject("100", e.getMessage()); //Promise 失败
}
}
@Nullable
@Override
public Map<String, Object> getConstants() {
final Map<String, Object> constants = new HashMap<>();
constants.put("URL", "http://www.rnapp.cc/");
constants.put("port", "8081");
constants.put("ip", "192.168.1.100");
constants.put("author", "RNAPP.CC");
return constants;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.i(TAG, "requestCode=" + requestCode + ", resultCode=" + resultCode);
if (requestCode!=REQUEST_CODE || resultCode!=RESULT_OK)
return;
Log.i(TAG, "在原生模块类里实现监听接口ActivityEventListener");
sendMsgToRn("在原生模块类里实现监听接口ActivityEventListener");
}
@Override
public void onNewIntent(Intent intent) {
}
@Override
public void onHostResume() {
Log.i(TAG, "onActivityResume");
}
@Override
public void onHostPause() {
Log.i(TAG, "onActivityPause");
}
@Override
public void onHostDestroy() {
Log.i(TAG, "onActivityDestroy");
}
}
index.android.js
/**
* Sample React Native App
* https://github.com/facebook/react-native
* @flow
*/
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
NativeModules,
DeviceEventEmitter
} from 'react-native';
class RNAPP extends Component {
componentWillMount(){
DeviceEventEmitter.addListener('AndroidToRNMessage', this.handleAndroidMessage);
}
componentWillunMount() {
DeviceEventEmitter.remove('AndroidToRNMessage', this.handleAndroidMessage);
}
handleAndroidMessage = (msg) => {
console.log(msg);
NativeModules.MyNativeModule.showNativeMsg('返回:' + msg + '\nPowered by RNAPP.CC');
}
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
Welcome to React Native!
</Text>
<Text style={styles.welcome}>
React Native混合原生开发
</Text>
<Text style={styles.instructions}>
Powered by rnapp.cc
</Text>
<Text onPress={this.callNative.bind(this)} style={styles.btn}>调用原生函数 - 消息机制</Text>
<Text onPress={this.callNativeCallback.bind(this)} style={styles.btn}>调用原生函数 - 回调函数</Text>
<Text onPress={this.callNativePromise.bind(this)} style={styles.btn}>调用原生函数 - Promise</Text>
<Text style={styles.welcome} >
Android原生暴露的常量
</Text>
<Text style={styles.instructions}>
URL: {NativeModules.MyNativeModule.URL}
</Text>
<Text style={styles.instructions}>
Author: {NativeModules.MyNativeModule.author}
</Text>
</View>
);
}
callNative() {
NativeModules.MyNativeModule.rnCallNative();
}
callNativeCallback = () => {
NativeModules.MyNativeModule.rnCallNativeCallback(
(msg) => {
console.log(msg);
},
(x, y, width, height) => {
console.log('坐标: (' + x + ',' + y + '), 宽: ' + width + ', 高: ' + height);
NativeModules.MyNativeModule.showNativeMsg('坐标: (' + x + ',' + y + '), 宽: ' + width + ', 高: ' + height);
}
);
}
callNativePromise = () => {
NativeModules.MyNativeModule.rnCallNativePromise('React Native Promise机制调用原生模块的方法').then(
(msg) => {
console.log('Promise成功:' + msg);
}
).catch(
(err) => {
console.log(err);
}
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
btn: {
marginTop: 20,
marginLeft: 10,
marginRight: 10,
width: 260,
height: 35,
backgroundColor: '#3BC1FF',
color: '#fff',
lineHeight: 24,
fontWeight: 'bold',
textAlign: 'center',
textAlignVertical:'center'
},
});
AppRegistry.registerComponent('RNAPP', () => RNAPP);
版权声明:
作者:Joe.Ye
链接:https://www.appblog.cn/index.php/2023/02/25/react-native-learning-rn-and-native-communication/
来源:APP全栈技术分享
文章版权归作者所有,未经允许请勿转载。
共有 0 条评论