RN與原生模塊互調

1、iOS與RN的互調

相對於Android來說,iOS的兩端互調比較簡單。先看iOS裏 ShowAlert的配置:

// .h
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>

NS_ASSUME_NONNULL_BEGIN

@interface ShowAlert : RCTEventEmitter<RCTBridgeModule>

@end

NS_ASSUME_NONNULL_END


// .m
#import "ShowAlert.h"

@interface ShowAlert ()
@property (nonatomic, assign) BOOL hasListener;
@end

static NSString * const EVENT_NAME = @"AlertDismiss";

@implementation ShowAlert

// 如果 RCT_EXPORT_MODULE 裏有傳參, 則按照傳參作爲模塊名, 否則是類名
RCT_EXPORT_MODULE(Alert)

// 原生調用RN
- (NSArray<NSString *> *)supportedEvents {
  return @[EVENT_NAME];
}

// 在添加第一個監聽函數時觸發
- (void)startObserving {
  self.hasListener = YES;
}

// 當該模塊的最後一個監聽者被移除, 或者釋放的時候 調用
- (void)stopObserving {
  self.hasListener = NO;
}

- (void)sendToRNWithInfo:(NSString *)info {
  // 只有在有監聽的情況下才發射事件, 防止資源浪費和警告
  if (self.hasListener) {
    [self sendEventWithName:EVENT_NAME body:info];
  }
}

- (void)in_showAlert:(NSString *)title content:(NSString *) content done:(RCTResponseSenderBlock)done {
  UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:title message:content preferredStyle: UIAlertControllerStyleAlert];
  UIAlertAction *defaultAction = [UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
    [self sendToRNWithInfo: @"確定"];
  }];
  UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
    [self sendToRNWithInfo: @"取消"];
  }];
  
  [alertVC addAction:defaultAction];
  [alertVC addAction:cancelAction];
  
  [[UIApplication sharedApplication].delegate.window.rootViewController presentViewController:alertVC animated:true completion:^{
    !done ?: done(@[@"已經展示了"]);
  }];
}

// 暴露的方法名
RCT_EXPORT_METHOD(showAlert:(NSString *)title content:(NSString *) content done:(RCTResponseSenderBlock)done) {
  [self in_showAlert:title content:content done:done];
}

// 導出的常量, 取值方式: 模塊.key
- (NSDictionary *)constantsToExport {
  return  @{
    @"one": @"大寫的一",
    @"two": @"大寫的二"
  };
}

// 重寫constantsToExport, 必須重寫此方法
+ (BOOL)requiresMainQueueSetup {
  return true;
}

// 涉及UI的, 表明初始化的線程, 默認爲background線程
- (dispatch_queue_t)methodQueue{
    return dispatch_get_main_queue();
}

@end

在 RN 裏使用:

import { Button } from '@ant-design/react-native'
import { memo, useEffect } from 'react'
import { NativeEventEmitter, NativeModules, View } from 'react-native'

// 獲取模塊名
const { Alert } = NativeModules
// 原生事件發射器
const alertEmitter = new NativeEventEmitter(Alert)

export default memo(function HomePage() {
  useEffect(() => {
    // 添加監聽, 用於被原生模塊調用
    const subscription = alertEmitter.addListener('AlertDismiss', info => {
      console.log('選擇的狀態info :>> ', info)
    })

    return () => {
      // 在組件卸載的時候移除監聽
      subscription.remove()
    }
  }, [])

  return (
    <View>
      <Button
        onPress={() => {
          // RN調用原生方法
          NativeModules.Alert.showAlert('這是標題', '這是內容', info => {
            console.log('error, events :>> ', info)
          })
        }}>
        調用原生模塊
      </Button>
    </View>
  )
})

2、Android與RN的互調

a. 編寫Module CalendarModule

package com.zxmall;

import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;

import java.util.HashMap;
import java.util.Map;

public class CalendarModule extends ReactContextBaseJavaModule {
  CalendarModule(ReactApplicationContext context) {
    super(context);
  }

  /**
   * 模塊名稱
   */
  @NonNull
  @Override
  public String getName() {
    return "CalendarModule";
  }

  /**
   * 導出常量, 取值: 模塊名.key 的方式
   * @return 暴露給RN的常量
   */
  @Nullable
  @Override
  public Map<String, Object> getConstants() {
    final Map<String, Object> constants = new HashMap<>();
    constants.put("one", "大寫的一");
    constants.put("two", "大寫的二");
    return constants;
  }

  /**
   * 給RN調用的原生方法
   * @param name RN傳過來的值
   * @param location RN傳過來的值
   * @param cb 回調給RN
   */
  @ReactMethod
  public void createCalendarEvent(String name, String location, Callback cb) {
    Log.i("calendar", name + location);

    cb.invoke("已經完成了打印");
  }
}

b. 編寫ReactPackage:CalendarPackage
在React native中註冊原生模塊: 將原生模塊添加到一個ReactPackage中,並在React native中註冊這個ReactPackage。
在初始化過程中,React Native將遍歷所有包,併爲每個ReactPackage註冊其中的每個原生模塊。

package com.zxmall;

import androidx.annotation.NonNull;
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 CalendarPackage implements ReactPackage {

  @NonNull
  @Override
  public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactApplicationContext) {
    List<NativeModule> modules = new ArrayList<>();
    modules.add(new CalendarModule(reactApplicationContext));
    return modules;
  }

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

c. 在 MainApplication 裏註冊。

在 RN 裏使用:

import { Button } from '@ant-design/react-native'
import { memo } from 'react'
import { NativeModules, View } from 'react-native'

export default memo(function HomePage() {
  return (
    <View>
      <Button
        onPress={() => {
          NativeModules.CalendarModule.createCalendarEvent(
            '這是標題',
            '這是地點',
            info => {
              console.log('安卓原生回調信息 :>> ', info)
            }
          )
        }}>
        調用原生模塊
      </Button>
    </View>
  )
})

參考

Android 原生模塊
iOS 原生模塊

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章