舊機寶開發筆記之:RN應用和Native應用之間的通信(入門)

前言

RN主要用於編寫UI,原生API的調用、網絡通信等等複雜的邏輯則大多是通過原生代碼去實現的。那麼RN和原生代碼是怎麼交互的呢?
官網在此
以下以安卓平臺原生爲例,我們按照官網的教程來實現這樣一個交互demo,藉此來了解互相調用的用法。不過我們的起點是一個剛剛創建的RN應用(通過

npx react-native init

來創建的),可能有些操作已經存在,不過這些步驟不會因此而被忽略。

思路

開始之前先來理清楚一下思路。
首先是RN調用原生接口:

  1. 第一步,很顯然的我們要在android裏實現一個方法,實現具體的邏輯供RN調用。
  2. 第二步,我們需要通過RN框架把這個方法“傳遞”給RN應用。
  3. 第三步,RN應用調用“傳遞”過來的方法,實現RN對原生的調用。

而原生對RN的調用則剛好相反。
那開始試試吧。

定義原生方法

假設我們要通過原生邏輯來顯示一個Toast(這種方式邏輯簡單、表現明顯),我們先定義一個ToastUtil類,並且爲其增加一個顯示Toast的方法show()。

public class ToastUtil {
    public void show(Context context,String message){
        Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
    }
}

將原生方法通過RN框架“傳給”RN應用

現在需要把我們的工具類交給RN並由其傳遞給RN應用了。分析官方示例,將工具類交給RN的路徑抽離如下圖所示:
在這裏插入圖片描述

  1. RN框架提供了一個ReactApplication接口,正如它名字想表達的一樣,這是一個要由android的Application實現的接口。
public interface ReactApplication {

  /** Get the default {@link ReactNativeHost} for this app. */
  ReactNativeHost getReactNativeHost();
}

其中要求實現方法getReactNativeHost。對於已經接入RN的android示例代碼來說,這些操作是現成的。

public class MainApplication extends Application implements ReactApplication {
@Override
public ReactNativeHost getReactNativeHost() {
  return reactNativeHost;
}}
  1. 所以需要創建一個ReactNativeHost對象給ReactApplication 。這些在繼承RN框架的時候也是現成的。不過我們發現RN框架本身似乎還有一些其他的Package要返回,那麼我們自己的只要add在後面就可以了。
ReactNativeHost reactNativeHost =new ReactNativeHost(this){
    @Override
    public boolean getUseDeveloperSupport() {
        return false;
    }

    @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 MyReactNativePackage());
      return packages;
    }
};
  1. ReactPackage是接口,我們想要返回自己的ReactPackage就需要實現下這個接口,需要實現的接口裏就包括了:createNativeModules
public interface ReactPackage {

  /**
   * @param reactContext react application context that can be used to create modules
   * @return list of native modules to register with the newly created catalyst instance
   */
  @NonNull
  List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext);

  /** @return a list of view managers that should be registered with {@link UIManagerModule} */
  @NonNull
  List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactContext);
}

  1. 所以就需要一個ReactContextBaseJavaModule交給我們剛剛實現的ReactPackage了。到這終於到了我們要“傳遞”的工具類出場了。沒錯,我們的工具類都要定義成“ReactContextBaseJavaModule”以方便RN框架去“傳遞”。

邏輯基本就是這樣了,那麼現在開始動手實現吧。
首先,就是改造我們剛纔的工具類,因爲要RN認識的特殊類才能被“傳輸”,將剛纔的ToastUtil繼承下ReactContextBaseJavaModule。多了個需要實現的類:getName

public class ToastUtil extends ReactContextBaseJavaModule {
    private static ReactApplicationContext reactApplicationContext;
    public ToastUtil(@NonNull ReactApplicationContext reactContext) {
        super(reactContext);
        reactApplicationContext=reactContext;
    }

    @ReactMethod
    public void show(String message){
        Toast.makeText(reactApplicationContext, message, Toast.LENGTH_SHORT).show();
    }

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

getName返回一個自定義的字符串,是當前這個工具類的標識符信息。RN應用就是通過這個字符串來找到它想要的工具類的。仔細觀察會發現show方法被註解了@ReactMethod,沒錯,希望在RN中調用的方法都要用這個註解來聲明下。這個時候突然發現了一個問題:RN中有Context這個上下文對象嗎?再看官網教程,果然直接用了一個傳進來的ReactApplicationContext,好吧,還是要傳進來並保存下這個ReactApplicationContext留作後用。(你可能會想直接從別的地方拿過來的Context不也行麼,結果是不傳ReactApplicationContext進來工具類無法正常使用,不僅是你,你實現的ReactContextBaseJavaModule也需要這麼一個Contex啊,所以就成了上面這個樣子了)
其次,需要實現一個ReactPackage,在它的createNativeModules方法中返回我們的工具類ToastUtil。

public class ToastPackage implements ReactPackage {
    @NonNull
    @Override
    public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext) {
        List<NativeModule> modules = new ArrayList<>();
        modules.add(new ToastUtil(reactContext));
        return modules;
    }

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

然後需要把我們自己的ReactPackage加到實現了ReactApplication的ReactNativeHost對象的實現方法getPackages裏。

@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 MyReactNativePackage());
    packages.add(new ToastPackage());
  return packages;
}

剩下的實現ReactAplication並返回ReactNativeHost的操作一早就已經默認實現了。現在終於把自己定義的工具類交給RN了。

在RN中調用原生方法

我們已經按照RN的要求定義了自己的工具類,並把它交給RN了,RN會幫助我們把這個類“傳輸”到RN應用中,接下來我們就要嘗試在RN應用中來調用了。

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

export default class App extends React.Component {
  _onPress() {
    NativeModules.ToastUtil.show('哈哈');
  }

  render(): React$Node {
    return (
      <View>
        <Button onPress={this._onPress} title={'顯示Toast'} />
      </View>
    );
  }
}

從原生傳過來的工具類都在NativeModules裏,所以我們需要先引入NativeModules。那麼怎麼定位到具體的我們的工具類呢?還記得實現工具類裏繼承了一個getName的方法麼,之前說它是那個工具類的標識符,現在派上用場了。之前返回的字符串是“ToastUtil”,那麼我們的工具類就是: NativeModules.ToastUtil了,可以直接在RN應用中通過

NativeModules.ToastUtil.show('哈哈');

來調到原生的ToastUtil.show(‘哈哈’)方法了。點擊按鈕就能看到熟悉的Toast了。
在這裏插入圖片描述
ok,到這入門就結束了。

總結

對於一個init出來的RN項目,快速實現RN對原生方法調用的操作如下:

  1. 創建一個類A繼承ReactContextBaseJavaModule,實現的getName返回字符串如“X”,可以使用構造方法獲取context,編寫自己的要提供給RN的方法y()並使用@ReactMethod註解自定義的方法。
  2. 新建一個類B實現ReactPackage接口,並把上面創建的工具類A在createNativeModules裏返回。
  3. 在已經實現了ReactApplication的Application裏,找到getReactNativeHost中返回的ReactNativeHost,並在其實現類的getPackages方法中,在返回的List上,增加上面創建的ReactPackage實現類B。
  4. 在RN中import NativeModules from ‘react-native’,並通過NativeModules.X.y()的方式調用原生方法。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章