一、react native版本环境:
“react”: “18.1.0”,
“react-native”: “0.70.6”,
“node”: "14.18.2
二、发送端步骤: 在A app
中注册一个Native Module模块,使用Intent
连接B app
,sendBroadcast
发送广播传递数据。
(1)首先在 /android/app/src/main/java/com/judgeapp
(包名judgeapp
以我自己的项目为例) 创建自定义的原生模块包,如下图所示:
(2)创建一个SendModule.java
自定义的原生模块,具体看代码:
package com.judgeapp.mymodule;
import android.widget.Toast;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
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;
public class SendModule extends ReactContextBaseJavaModule {
public SendModule(ReactApplicationContext reactContext) {
super(reactContext);
}
@Override
public String getName() {
return "SendModule";
}
// 向另外一个App发送数据的代码逻辑,发送端通过 Intent连接 B app,
@ReactMethod
public void sendData(String data) {
Intent intent = new Intent();
intent.setAction("com.customerapp.ACTION_RECEIVE"); // 对应接收方的 receiver 广播接收器的 action
// Log.d("judgeApp", data: " + data);
intent.putExtra("message", data); // 在intent中添加发送数据的键值对
intent.setPackage("com.customerapp"); // 指定另外一个App的接收Intent的应用包名
getReactApplicationContext().sendBroadcast(intent); // sendBroadcast 发送广播
}
}
(3)创建一个SendModulePackage.java
模块包(ReactPackage),用于将自定义的原生模块SendModule.java
注册到React Native应用程序中。代码如下:
package com.judgeapp.mymodule;
import com.judgeapp.mymodule.SendModule;
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;
import android.util.Log;
public class SendModulePackage implements ReactPackage {
private static final String TAG = "MainActivity";
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new SendModule(reactContext)); // 将自定义的原生模块添加到模块包中
return modules;
}
}
(4)在MainApplication.java
中注册SendModulePackage.java
模块包,代码如下:
package com.judgeapp;
import com.judgeapp.mymodule.SendModulePackage; // <-- 引入你自己的包
import android.app.Application;
import android.content.Context;
import com.facebook.react.PackageList;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.config.ReactFeatureFlags;
import com.facebook.soloader.SoLoader;
import com.judgeapp.newarchitecture.MainApplicationReactNativeHost;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
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() {
@SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for example:
packages.add(new SendModulePackage());
return packages;
}
@Override
protected String getJSMainModuleName() {
return "index";
}
};
private final ReactNativeHost mNewArchitectureNativeHost =
new MainApplicationReactNativeHost(this);
@Override
public ReactNativeHost getReactNativeHost() {
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
return mNewArchitectureNativeHost;
} else {
return mReactNativeHost;
}
}
@Override
public void onCreate() {
super.onCreate();
// If you opted-in for the New Architecture, we enable the TurboModule system
ReactFeatureFlags.useTurboModules = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
SoLoader.init(this, /* native exopackage */ false);
initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
}
/**
* Loads Flipper in React Native templates. Call this in the onCreate method with something like
* initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
*
* @param context
* @param reactInstanceManager
*/
private static void initializeFlipper(
Context context, ReactInstanceManager reactInstanceManager) {
if (BuildConfig.DEBUG) {
try {
/*
We use reflection here to pick up the class that initializes Flipper,
since Flipper library is not available in release mode
*/
Class<?> aClass = Class.forName("com.judgeapp.ReactNativeFlipper");
aClass
.getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
.invoke(null, context, reactInstanceManager);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
主要是packages.add(new SendModulePackage())
这块代码
(5)在前端使用,代码如下:
// 引入 NativeModules
import { NativeModules } from 'react-native';
// SendModule 为我们自定义的原生模块
const SendModule = NativeModules.SendModule;
console.log('NativeModules: ', NativeModules);
...
// sendData 是在原生模块自定义的方法,默认数据格式为字符串
SendModule.sendData(JSON.stringify(res.data))
...
三、接收端: 在B app
中添加一个广播接收器(BroadcastReceiver
),用于接收特定动作(Action
)的广播并将数据传递给React Native应用程序。
(1)首先在 /android/app/src/main/java/com/customerapp (包名customerapp 以我自己的项目为例) 创建一个MyBroadcastReceiver.java
广播接收器,如下图所示:
(2)MyBroadcastReceiver.java
广播接收器,在这个特定的类中,它的过滤器是设置为接收com.customerapp.ACTION_RECEIVE
动作的广播。因此,当发送一个带有这个动作的广播时,这个类的onReceive
方法就会被执行,具体代码如下:
// MyBroadcastReceiver.java
package com.customerapp;
import com.customerapp.MainApplication;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.bridge.Arguments;
public class MyBroadcastReceiver extends BroadcastReceiver {
private ReactContext reactContext = MainApplication.getReactContext();
private String messageKey = "message";
public MyBroadcastReceiver() {
// 默认的无参数构造函数
}
// 通过匹配对应的 action 接收 A app传递过来的数据
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("com.customerapp.ACTION_RECEIVE")) {
// intent.getStringExtra 获取之前 A app intent.putExtra("message", data)发送的数据
String receivedMessage = intent.getStringExtra(messageKey);
Log.d("customerApp", "reactContext: " + reactContext + "__>>>receivedMessage:" +receivedMessage);
WritableMap params = Arguments.createMap();
params.putString(messageKey, receivedMessage);
// 原生模块通过emit将数据传递给前端,前端通过 DeviceEventEmitter 监听获取数据
reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("onMessageReceived", params);
}
}
}
(3)上面的 reactContext
是需要在 MainApplication.java
类中设置的,MainApplication.java
全部代码如下:
package com.customerapp;
import com.customerapp.mymodule.SendModulePackage; // <-- 引入你自己的包
import android.app.Application;
import android.content.Context;
import com.facebook.react.PackageList;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.config.ReactFeatureFlags;
import com.facebook.soloader.SoLoader;
import com.customerapp.newarchitecture.MainApplicationReactNativeHost;
import com.facebook.react.bridge.ReactContext;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import android.util.Log;
public class MainApplication extends Application implements ReactApplication {
private static ReactContext reactContext;
private final ReactNativeHost mReactNativeHost =
new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
@SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for example:
packages.add(new SendModulePackage());
return packages;
}
@Override
protected String getJSMainModuleName() {
return "index";
}
};
private final ReactNativeHost mNewArchitectureNativeHost =
new MainApplicationReactNativeHost(this);
@Override
public ReactNativeHost getReactNativeHost() {
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
return mNewArchitectureNativeHost;
} else {
return mReactNativeHost;
}
}
@Override
public void onCreate() {
super.onCreate();
// If you opted-in for the New Architecture, we enable the TurboModule system
ReactFeatureFlags.useTurboModules = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
SoLoader.init(this, /* native exopackage */ false);
initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
}
public static ReactContext getReactContext() {
return reactContext;
}
/**
* Loads Flipper in React Native templates. Call this in the onCreate method with something like
* initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
*
* @param context
* @param reactInstanceManager
*/
private static void initializeFlipper(
Context context, ReactInstanceManager reactInstanceManager) {
if (BuildConfig.DEBUG) {
try {
/*
We use reflection here to pick up the class that initializes Flipper,
since Flipper library is not available in release mode
*/
Class<?> aClass = Class.forName("com.customerapp.ReactNativeFlipper");
aClass
.getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
.invoke(null, context, reactInstanceManager);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
reactInstanceManager.addReactInstanceEventListener(new ReactInstanceManager.ReactInstanceEventListener() {
@Override
public void onReactContextInitialized(ReactContext context) {
// 将ReactContext赋值给静态变量
// MainApplication.reactContext = context;
reactContext = context;
// Log.d("customerApp", "reactContext111: " + reactContext);
// Log.d("customerApp", "MainApplication.reactContext111: " + MainApplication.reactContext);
}
});
reactInstanceManager.createReactContextInBackground();
}
}
设置reactContext
重要代码为:
// 抛出方法,在 MyBroadcastReceiver.java 中引入
public static ReactContext getReactContext() {
return reactContext;
}
// 在此方法中设置 reactContext
private static void initializeFlipper(
Context context, ReactInstanceManager reactInstanceManager) {
...
//注册了一个ReactInstanceEventListener监听器,因为要在React上下文初始化完成时执行相应的操作,否则 context 为 null
reactInstanceManager.addReactInstanceEventListener(new ReactInstanceManager.ReactInstanceEventListener() {
@Override
public void onReactContextInitialized(ReactContext context) {
// 将ReactContext赋值给静态变量
reactContext = context;
}
});
// 调用reactInstanceManager.createReactContextInBackground()方法来在后台创建React上下文
reactInstanceManager.createReactContextInBackground();
}
(4)在 AndroidManifest.xml
设置广播接收器,代码如下:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.customerapp">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.RECORD_VIDEO"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
// 广播接收器的权限设置
<uses-permission android:name="android.permission.BROADCAST_SEND" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application
android:name=".MainApplication"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:allowBackup="true"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
android:launchMode="singleTask"
android:windowSoftInputMode="stateAlwaysHidden|adjustPan"
android:exported="true">
<intent-filter>
<action android:name="com.customerapp.ACTION_SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="customerapp" android:host="message" />
</intent-filter>
</activity>
<receiver
android:name=".MyBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.customerapp.ACTION_RECEIVE" />
</intent-filter>
</receiver>
</application>
</manifest>
注意: 这个广播接收器的action
对应为 上文中发送端 设置的action
intent.setAction(“com.customerapp.ACTION_RECEIVE”); // 对应接收方的 receiver
广播接收器的 action
(5)在前端中使用,代码如下:
import {
DeviceEventEmitter
} from 'react-native';
...
// 在componentDidMount 中监听 onMessageReceived 获取数据
componentDidMount () {
this.state.subscription = DeviceEventEmitter.addListener('onMessageReceived', (params) => {
console.log('Received message:', params.message);
let data = JSON.parse(params.message)
this.setState({
isStart: true,
judgeInfo: data,
list: data.mybusiness,
})
store.dispatch(setJudgeInfo(data));
});
}
componentWillUnmount () {
this.state.subscription.remove();
}
...
本文是我第一次开发此种功能实践完后的总结,若哪里不妥,欢迎大佬多多指教。