RN与原生模块互调

2022-12-19  本文已影响0人  春暖花已开
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 原生模块

上一篇下一篇

猜你喜欢

热点阅读