[譯] React Native 與 iOS 和 Android 通訊

React Native 流行的最大緣由之一是咱們能夠在 Native 語言和 JavaScript 代碼之間創建橋樑。這意味着咱們能夠複用在 iOS 和 Android 中建立的全部可重用庫。前端

要建立一個商業級的應用程序,您須要使用 Native Bridge。React Native 能夠同時在 iOS 和 Android 上運行,但關於它如何跨平臺的文章教程很是少。在本文中,咱們將建立一個 Native Bridge,以便從 JavaScript 訪問 Swift 和 Java 類。java

文本是此係列的第一部分,第二部分能夠在 Native Bridge of UI component 中找到 這裏react

代碼能夠在這裏找到 -> github.com/nalwayaabhi…android

建立一個 LightApp(手電筒)

爲了更好地理解 Native Module,咱們將使用 react-native CLI 建立一個簡單的 LightApp 示例。ios

$ react-native init LightApp
$ cd LightApp
複製代碼

接下來,咱們將在 Swift 和 Java 中建立一個 Bulb 類,稍後將在 React 組件中使用它。這是一個跨平臺的示例,相同的 React 代碼將同時適用於iOS和Android。git

如今咱們已經建立了項目的基本框架,接下來咱們將本文分爲兩部分:github

第一節 — 與原生 iOS 通訊swift

第二節 — 與原生 Android 通訊後端

第一節 — 與原生 iOS 通訊

在本節中,咱們將重點關注 iOS,瞭解如何在 Swift/Objective C 和 React 組件間創建橋樑。有如下三個步驟:react-native

步驟 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 {

    @objc
    static var isOn = false

    @objc
    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)
@end
複製代碼

除非顯式地指定,不然 React Native 不會將任何 Bulb 中的函數暴露給 React JavaScript。爲此,咱們使用了 RCT_EXPORT_METHOD() 宏。因此咱們已經暴露了 Bulb 類和 turnOn 函數給了咱們的 Javascript 代碼。因爲 Swift 對象被轉換爲了 Javascript 對象,所以其中必定存在一種對應關係。RCT_EXPORT_METHOD 支持全部標準 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();
  }
  render() {
  return (
    <View style={styles.container}>
    <Text style={styles.welcome}>Welcome to Light App!!</Text>
    <Button
        onPress={this.turnOn}
    title="Turn ON "
    color="#FF6347" />
    </View>
  );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    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 並添加此函數。

@objc
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 {
    @objc
    static var isOn = false

    @objc
    func turnOn() {
        Bulb.isOn = true
        print("Bulb is now ON")
    }

    @objc
    func turnOff() {
        Bulb.isOn = false
        print("Bulb is now OFF")
    }

    @objc
    func getStatus(_ callback: RCTResponseSenderBlock) {
        callback([NSNull(), Bulb.isOn])
    }

    @objc
        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)
@end
複製代碼

咱們已將 (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});
    })
}

render() {
    return (
    <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"
    /> :
    <Button
    onPress={this.turnOff}
    title="Turn OFF "
    color="#FF6347"
    /> }
    </View>
    );
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#F5FCFF',
    },
});
複製代碼

從新變異代碼並運行應用程序,您能夠看到 Bulb Status 的值,當您單擊 Turn ON 時,它將顯示 Bulb 爲 ON

請記住從新編譯代碼而不是刷新,由於咱們更改了原生代碼。

Section 2 — Native Bridge in Android

在本節中,咱們將使用與 iOS 相同的 Javascript 代碼,它一樣能夠應用在 Android 中。此次咱們將在 Java 中建立 Bulb 類並將相同的函數 turnOn, TurnOffgetStatus 暴露給 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");
    }

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

}
複製代碼

咱們建立了一個 Bulb Java 類,它繼承自 ReactContextBaseJavaModuleReactContextBaseJavaModule 要求必定要實現名一個爲 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  {
    @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 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 {
...

    @Override
    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 連接。


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索