react native 使用Native Module、Intent、广播接收器 实现正副双屏的两个app之间的相互通信

一、react native版本环境:

“react”: “18.1.0”,
“react-native”: “0.70.6”,
“node”: "14.18.2

二、发送端步骤:A app中注册一个Native Module模块,使用Intent 连接B appsendBroadcast 发送广播传递数据。
(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();
}
...

本文是我第一次开发此种功能实践完后的总结,若哪里不妥,欢迎大佬多多指教。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值