[译] React Native 与iOS 和Android 通信

android studio 教程 | 2019-02-23 11:37

原文地址:React Native Bridge for iOS and Android

原文作者:Abhishek Nalwaya

译文出自:掘金翻译计划

本文永久链接:-miner/blob/master/TODO1/react-native-bridge-for-ios-and-android.md

译者:MeandNi

校对者:lsvih

React Native 流行的最大原因之一是我们可以在 Native 语言和 JavaScript 代码之间建立桥梁。这意味着我们可以复用在 iOS 和 Android 中创建的所有可重用库。

要创建一个商业级的应用程序,您需要使用 Native Bridge。React Native 可以同时在 iOS 和 Android 上运行,但关于它如何跨平台的文章教程非常少。在本文中,我们将创建一个 Native Bridge,以便从 JavaScript 访问 Swift 和 Java 类。

文本是此系列的第一部分,第二部分可以在 Native Bridge of UI component 中找到 这里。

代码可以在这里找到 ->

为了更好地理解 Native Module,我们将使用 react-native CLI 创建一个简单的 LightApp 示例。

$ react-native init LightApp

$ cd LightApp

接下来,我们将在 Swift 和 Java 中创建一个 Bulb 类,稍后将在 React 组件中使用它。这是一个跨平台的示例,相同的 React 代码将同时适用于iOS和Android。

现在我们已经创建了项目的基本框架,接下来我们将本文分为两部分:

第一节 — 与原生 iOS 通信

第二节 — 与原生 Android 通信

在本节中,我们将重点关注 iOS,了解如何在 Swift/Objective C 和 React 组件间建立桥梁。有以下三个步骤:

步骤 1) 创建一个 Bulb 类 并且完整初步通信

步骤 2) 理解 GCD Queue 并且解决出现的警告

步骤 3) 从 Swift 和 Callbacks 访问 JavaScript 中的变量

步骤 1) 创建一个 Bulb 类 并且完成初步通信

首先,我们将在 swift 中创建一个 Bulb 类,它将具有一个静态类变量 isOn 和一些其他函数。然后我们将从 Javascript 访问这个 swift 类。让我们首先在 ios 文件夹中打开 LightApp.xcodeproj 文件。此时 Xcode 应该会被打开。

在 Xcode 中打开项目后,创建一个新的 Swift文件 Bulb.swift,如下所示:

我们还要点击 Create Bridging Header,创建一个文件 LightApp-Bridging-Header.h 它将有助于 Swift 和 Objective C 代码之间的通信。请记住,在项目中,我们只有一个 Bridge Header 文件。因此,如果我们添加新文件,我们可以重用此文件。

将以下代码加入 -Bridging-Header.h 文件:

#import "React/RCTBridgeModule.h"

RCTBridgeModul 将提供一个接口,用于注册 Bridge 模块。

接下来将以下代码输入 Bulb.swift:

import Foundation

@objc(Bulb)

class Bulb: NSObject {

static var isOn = false

func turnOn() {

Bulb.isOn = true

print("Bulb is now ON")

我们创建了 Bulb 类,它继承自 NSObject。大多数 Objective-C 类的根类是 NSObject,子类从该类继承运行时系统的基本接口,因此它们有与 Objective-C 对象相同的能力。我们在函数和类之前使用了 @objc,这将使那个类,方法或对象可用于 Objective C。

@objc 注解使您的 Swift API 在 Objective-C 和 Objective-C 运行时可用。

现在选择 File -> New -> File 创建一个新文件,然后选择 Objective-C 文件,然后将该文件命名为 Bulb.m并添加以下代码:

#import "React/RCTBridgeModule.h"

@interface RCT_EXTERN_MODULE(Bulb, NSObject)

RCT_EXTERN_METHOD(turnOn)

除非显式地指定,否则 React Native 不会将任何 Bulb 中的函数暴露给 React JavaScript。为此,我们使用了 RCTEXPORTMETHOD() 宏。所以我们已经暴露了 Bulb 类和 turnOn 函数给了我们的 Javascript 代码。由于 Swift 对象被转换为了 Javascript 对象,因此其中一定存在一种对应关系。RCTEXPORTMETHOD 支持所有标准 JSON 对象类型:

NSString 对应 string

NSInteger、float、double、CGFloat、NSNumber 对应 number

BOOL 对应 boolean

NSArray 对应 array

NSDictionary 对应 包含此列表中的字符串键和任何类型的值的对象

RCTResponseSenderBlock 对应 function

现在让我们更新 JavaScript 代码并从我们的 React 组件访问这个 Bulb 类。为此,请打开 App.js 并更新为以下代码:

import React, {Component} from 'react';

import {StyleSheet, Text, View, NativeModules, Button} from 'react-native';

export default class App extends Component{

turnOn = () => {

NativeModules.Bulb.turnOn();

<View style={styles.container}>

<Text style={styles.welcome}>Welcome to Light App!!</Text>

onPress={this.turnOn}

title="Turn ON "

color="#FF6347" />

</View>

const styles = StyleSheet.create({

container: {

justifyContent: 'center',

alignItems: 'center',

backgroundColor: '#F5FCFF',

现在运行iOS模拟器:

现在打开 Xcode 控制台查看日志,我们可以看到从 JavaScript 代码调用 Swift turnOn 方法。(因为我们已经看到了方法中执行的日志)

步骤 2) 理解 GCD Queue并且解决出现的警告

现在让我们修复模拟器底部和浏览器控制台中显示的警告:

Bulb 模块需要主队列设置,因为它覆盖 init但 没有实现 requiresMainQueueSetup。在以后的版本中,React Native 将默认初始化后台线程上的所有原生模块,除非明确选择不需要。

为了更好地理解,让我们了解 React Native 运行的所有线程:

Main thread:UI 渲染执行的线程

Shadow queue:布局发生的地方

JavaScript thread:JS 代码实际执行的地方

除非另有说明,否则每个原生模块都有自己的 GCD 队列。 现在,由于这个原生模块将在不同的线程上运行,并且我们的主线程依赖于它,它会显示此警告。 要使此代码在 MainQueue 上运行,请打开 Bulb.swift 并添加此函数。

static func requiresMainQueueSetup() -> Bool {

return true

您可以明确提及 return false 以让它在单独的线程中运行。

步骤 3) 从Swift和Callbacks访问JavaScript中的变量

现在让我们将 Bulb 的开关(ON 或 OFF)值添加到 React 屏幕。为此,我们将 getStatus 函数添加到 Bulb.swift 并从 JavaScript 代码调用该方法。 我们将创建此方法作为回调。

React Native 桥是异步的,因此将结果传递给 JavaScript 的唯一方法是使用回调或触发事件

让我们用粗体更新 Bulb.swift 中的代码:

@objc(Bulb)

class Bulb: NSObject {

static var isOn = false

func turnOn() {

Bulb.isOn = true

print("Bulb is now ON")

func turnOff() {

Bulb.isOn = false

print("Bulb is now OFF")

func getStatus(_ callback: RCTResponseSenderBlock) {

callback([NSNull(), Bulb.isOn])

static func requiresMainQueueSetup() -> Bool {

return true

getStatus() 方法接收一个我们将从您的 JavaScript 代码传递的回调参数。我们用值数组调用了回调函数,这些函数将暴露在 JavaScript 中。我们已经将 NSNull() 作为第一个元素传递,我们将其视为回调中的错误。

我们需要将这个 Swift 方法暴露给 JavaScript,所以添加下方的粗体行代码到 Bulb.m 中:

@interface RCT_EXTERN_MODULE(Bulb, NSObject)

RCT_EXTERN_METHOD(turnOn)

RCT_EXTERN_METHOD(turnOff)

RCT_EXTERN_METHOD(getStatus: (RCTResponseSenderBlock)callback)

我们已将 (RCTResponseSenderBlock)callback 暴露为函数 getStatus 的参数

然后最后更新React代码:

import React, {Component} from 'react';

import {StyleSheet, Text, View, NativeModules, Button} from 'react-native';

export default class App extends Component{

constructor(props) {

super(props);

this.state = { isOn: false };

this.updateStatus();

turnOn = () => {

NativeModules.Bulb.turnOn();

this.updateStatus()

turnOff = () => {

NativeModules.Bulb.turnOff();

this.updateStatus()

updateStatus = () => {

NativeModules.Bulb.getStatus( (error, isOn)=>{

this.setState({ isOn: isOn});

<View style={styles.container}>

<Text style={styles.welcome}>Welcome to Light App!!</Text>

<Text> Bulb is {this.state.isOn ? "ON": "OFF"}</Text>

{!this.state.isOn ? <Button

onPress={this.turnOn}

title="Turn ON "

color="#FF6347"

onPress={this.turnOff}

title="Turn OFF "

color="#FF6347"

</View>

const styles = StyleSheet.create({

container: {

justifyContent: 'center',

alignItems: 'center',

backgroundColor: '#F5FCFF',

重新变异代码并运行应用程序,您可以看到 Bulb Status 的值,当您单击 Turn ON 时,它将显示 Bulb 为 ON

请记住重新编译代码而不是刷新,因为我们更改了原生代码。

在本节中,我们将使用与 iOS 相同的 Javascript 代码,它同样可以应用在 Android 中。这次我们将在 Java 中创建 Bulb 类并将相同的函数 turnOn, TurnOff 和 getStatus 暴露给 Javascript。

打开 Android Studio 并单击 打开现有的 Android Studio 项目,然后在 LightApp 中选择 android 文件夹。下载所有 gradle 依赖项后,创建一个 Java 类 Bulb.java,如下所示:

并将 Bulb.java 中代码更新为:

package com.lightapp;

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;

import com.facebook.react.bridge.Callback;

public class Bulb extends ReactContextBaseJavaModule  {

private static Boolean isOn = false;

public Bulb(ReactApplicationContext reactContext) {

super(reactContext);

@ReactMethod

public void getStatus(

Callback successCallback) {

successCallback.invoke(null, isOn);

@ReactMethod

public void turnOn() {

isOn = true;

System.out.println("Bulb is turn ON");

@ReactMethod

public void turnOff() {

isOn = false;

System.out.println("Bulb is turn OFF");

public String getName() {

return "Bulb";

我们创建了一个 Bulb Java 类,它继承自 ReactContextBaseJavaModule 。 ReactContextBaseJavaModule要求一定要实现名一个为 getName 的函数。此方法的作用是返回在 JavaScript 中表示此类的 NativeModule 的字符串名称。所以在这里我们将调用 Bulb ,以便我们可以通过 JavaScript 中的 React.NativeModules.Bulb 来访问它。我们可以使用其它不同的名称代替 Bulb。

并非所有函数都显式地暴露给 Javascript,要向 JavaScript 公开函数,必须使用 @ReactMethod 注解 Java 方法。桥接方法的返回类型始终为 void。

我们还创建了一个 getStatu 函数,它具有参数作为回调,它返回一个 callback 并传递静态变量 isOn 的值。

下一步是注册模块,如果模块未注册,则无法从 JavaScript 获得。通过单击菜单"文件” ->“新建” -> “Java 类”并将文件名设置为 BulbPackage 来创建文件,然后单击“确定”。然后将以下代码添加到 BulbPackage.java

package com.lightapp;

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;

public class BulbPackage implements ReactPackage  {

public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {

return Collections.emptyList();

public List<NativeModule> createNativeModules(

ReactApplicationContext reactContext) {

List<NativeModule> modules = new ArrayList<>();

modules.add(new Bulb(reactContext));

return modules;

我们需要覆盖 createNativeModules 函数并将 Bulb 对象添加到 modules 数组中。如果这里没有添加,那么它将无法在 JavaScript 中使用。

需要在 MainApplication.java 文件的 getPackages 方法中提供 BulbPackage 包。此文件存在于 react-native 应用程序目录中的 android 文件夹下。在 android/app/src/main/java/com/LightApp/MainApplication.java中更新以下代码

public class MainApplication extends Application implements ReactApplication {

protected List<ReactPackage> getPackages() {

return Arrays.<ReactPackage>asList(

new MainReactPackage(),

new BulbPackage()

我们不需要更改在 iOS 中编写的任何 JavaScript 代码,因为我们已经暴露了相同的类名和函数。如果您已跳过 iOS 部分,则需要从 App.js 复制 React Javascript 代码。

现在通过 Android Studio 或 react-native run-android 运行 App:

哇唔!我们可以在屏幕上看到 Bulb 状态,并可以从按钮切换 ON 或 OFF。最棒的是我们创建了一个跨平台的应用。

如果发现译文存在错误或其他需要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 本文永久链接 即为本文在 GitHub 上的 MarkDown 链接。

掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 Android、iOS、前端、后端、区块链、产品、设计、人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划、官方微博、知乎专栏。