在之前的文章『「ZXing」实现 Android 扫描二维码』中提到,由于我在 React Native 中找不到可用的扫描二维码的第三方组件,所以被迫使用 Android 原生来实现该功能,那么就会出现一个新的问题,通过原生 Android 扫码得到的数据,如何传给 React Native 进行处理呢?

其实原生 Android 扫码后,继续由原生 Android 进行处理也是可以的,这样就避免了再次把数据回传的步骤,但是既然我使用的是 React Native,那么看重的就是其跨平台的特性,即既可以在 Android 系统中运行,也可以在 iOS 系统中运行。

也就是说,如果我在 Android 中进行扫码后继续由原生 Android 做逻辑处理,那我同样需要在 iOS 开发的时候做一套类似的逻辑处理。这无疑是加大时间成本来做相同的事情,与代码复用性相悖了。

所以,这就涉及到了原生 Android 和 React Native 交互的问题(如果以后我尝试做 iOS 开发的话会考虑原生 iOS 和 React Native 的交互)。

话不多说,来看看如何实现。

首先在项目中创建 MyIntentModule 类,并继承 ReactContextBaseJavaModule,这是原生 Activity 和 React 的交互模块。

/**
 * 原生 Activity 与 React 交互模块
 */
public class MyIntentModule extends ReactContextBaseJavaModule {

    public MyIntentModule(ReactApplicationContext reactContext) {
        super(reactContext);
    }

    @Override
    public String getName() {
        return "IntentMoudle";
    }

    @ReactMethod
    public void startActivityFromJS(String name, String params) {
        try {
            Activity currentActivity = getCurrentActivity();
            if (null != currentActivity) {
                Class toActivity = Class.forName(name);
                Intent intent = new Intent(currentActivity, toActivity);
                intent.putExtra("params", params);
                currentActivity.startActivity(intent);
            }
        } catch (Exception e) {
            throw new JSApplicationIllegalArgumentException("Could not open Activity: " + e.getMessage());
        }
    }

    @ReactMethod
    public void dataToJS(Callback successBack, Callback errorBack) {
        try {
            Activity currentActivity = getCurrentActivity();
            String result = currentActivity.getIntent().getStringExtra("data");
            if (TextUtils.isEmpty(result)) {
                result = "没有数据";
            }
            successBack.invoke(result);
        } catch (Exception e) {
            errorBack.invoke(e.getMessage());
        }
    }

    @ReactMethod
    public void startActivityForResult(String activityName, int requestCode, Callback successCallback, Callback erroCallback) {
        try {
            Activity currentActivity = getCurrentActivity();
            if (null != currentActivity) {
                Class aimActivity = Class.forName(activityName);
                Intent intent = new Intent(currentActivity, aimActivity);
                currentActivity.startActivityForResult(intent, requestCode);
                String result = (String) myBlockingQueue.take();
                successCallback.invoke(result);
            }
        } catch (Exception e) {
            erroCallback.invoke(e.getMessage());
            throw new JSApplicationIllegalArgumentException("Could not open the Activity: " + e.getMessage());
        }
    }
}

这里需要记住 getName() 方法中的命名名称,在 React Native 中调用需要用到,还要记得在相应的方法中添加 @ReactMethod 的注解,否则该方法将不被添加到 React Native 中。

然后在项目中创建 MyReactPackage 类,实现 ReactPackage 接口暴露给 React Native 调用,在 createNativeModules() 里注册上一步添加的模块:

/**
 * 注册模块
 * 实现 ReactPackage 接口暴露给 React Native 调用
 */
public class MyReactPackage implements ReactPackage {

    // 注册 MyIntentModule 添加的交互模块
    @Override
    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
        return Arrays.<NativeModule>asList(new MyIntentModule(reactContext));
    }

    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
        return Collections.emptyList();
    }
}

再在项目的 MainApplicationgetPackages() 方法中注册 MyReactPackage 模块:

@Override
protected List<ReactPackage> getPackages() {
    return Arrays.<ReactPackage>asList(
        new MainReactPackage(),
        new MyReactPackage()      // 注册 MyReactPackage 模块
    );
}

这样在 Android 端的配置就基本完成了,你可能会问 Activity 呢?

其实上文提到我是用在二维码功能上,所以就用『ZXing』中的 CaptureActivity 来作为目标 Activity

接下来就是在 React Native 中的调用了:

export default class FootBar extends Component {
  _onPressScanBTN() {
    NativeModules.IntentMoudle.startActivityForResult("com.google.zxing.activity.CaptureActivity", 100,
      (successMsg) => {alert(successMsg)},
      (erroMsg) => {alert(erroMsg)}
    );
  }
  render() {
    return (
      <View style={styles.container}>
        <ImageBackground style={styles.footbarbg} source={require('./images/footbar/footbar_background.png')}>
          ...
          <TouchableOpacity onPress={this._onPressScanBTN}>
            <Image style={styles.scanbtn} source={require('./images/footbar/scan_btn.png')} />
          </TouchableOpacity>
          ...
        </ImageBackground>
      </View>
    );
  }
}

我在这里就将扫描的结果返回并弹窗提醒,当然实际上我们是将回传的数据作其他操作的,这里只是作简单演示。

效果如下:

效果

该 Demo 已上传至 Github,详情可👉戳此处👈查看。