Blog

Blog

PHODAL

React Native 重新封装 Cordova 插件笔记:插件编写与第三方 SDK 编译

最近刚完成项目上的几个 React Native 插件的编写,便想记录这其中遇到的坑——主要是封装第三方 SDK 的问题。记录的过程中,顺便也就写一下相关的插件编写了。因此,本文分成三部分:

  • React Native 插件封装
  • Native 主动向 React Native 发送数据
  • React Native 编译问题

React Native 插件封装

先让我们从 React Native 端的 JS 封装代码看起。假设我们的 SDK 名字是 SDK,那么对应在 React Native 上的 sdk.js 代码如下所示:

import { NativeModules } from 'react-native';

module.exports = NativeModules.SDK;

实际上,在这里,我们应该列出所有的方法。但是,由于时间限制原因,就没有在这一一列出了。

React Native 插件 Android 示例

对应的,我们在 Android 端,只需要在方法开发之前添加 @ReactMethod 注解,再调用 promise 方法即可:

@ReactMethod
public void isLogined(Promise promise) {
    try {
        promise.resolve(SDK.app().is_logined());
    } catch (Exception e) {
        promise.reject(e);
    }
}

React Native Android 插件带参数示例

@ReactMethod
public void login(int userid, String signature, Promise promise) {

}

而在 iOS 上则稍微麻烦一些。

React Native 插件 iOS 示例

RN 对于 iOS 和 Android 的封装方式,到底还是因为两个系统的不同,而有所差异。如下是头文件 sdk.h 的代码:

#import "RCTBridgeModule.h"

@interface SDK : NSObject <RCTBridgeModule>


@end

相应的代码文件 sdk.m 则如下所示:

@implementation SDK

RCT_EXPORT_MODULE();
RCT_EXPORT_METHOD(isLogined:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject) {
    BOOL isLogin = [[SDKHelper sharedHelper] isLogin];
    resolve(@{@"isLogined": @(isLogin)});
}

从代码上来看,iOS 的 promise 使用方式与 Android 还是有些差异的。

React Native iOS 插件带参数示例

RCT_EXPORT_METHOD(login:(int64_t)userID
                  password:(NSString *) password
                  resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject) {

    [[SDKHelper sharedHelper] login:userID password:password];

    resolve(@"true");
}

Native 主动向 React Native 发送数据

在我们的需求里,还会遇到向 React Native 发送数据的功能。这个时候需要用到 EventEmitter,用于监听来自原生代码的示例。如下是两个不同的平台监听的代码:

webviewOnLoad = () => {
  if(Platform.OS === 'android') {
    DeviceEventEmitter.addListener('SDK_EVENT', this.handleNativeInvoke);
  } else {
    var RCTSDK = new NativeEventEmitter(NativeModules.SDK);
    RCTSDK.addListener('SDK_EVENT', this.handleNativeInvoke)
  }
}

这个时候就需要继承 RCTEventEmitter,其头文件如下所示:

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


@interface SDK : RCTEventEmitter <RCTBridgeModule>

@end

代码文件则如下:

#import <React/RCTBridge.h>

@implementation SDK

@synthesize bridge = _bridge;

- (NSArray<NSString *> *) supportedEvents
{
    return @[@"SDK_EVENT"];
}

RCT_EXPORT_MODULE();


- (void)pushEvent:(NSString *)formatString eventName:(NSString *)eventName JSONObject:(id)JSONObject {
    NSData* JSONObjectData = [NSJSONSerialization dataWithJSONObject:[JSONObject copy]
                                                             options:NSJSONWritingPrettyPrinted
                                                               error:nil];
    NSString* JSONObjectString = [[NSString alloc] initWithData:JSONObjectData
                                                       encoding:NSUTF8StringEncoding];
    NSString* javascript = [NSString stringWithFormat:formatString, eventName, JSONObjectString];
    dispatch_async(dispatch_get_main_queue(), ^{
        [self sendEventWithName:@"SDK_EVENT" body: @{@"injectedJS": javascript}];
    });
}

@end

我们需要使用如下的代码,以此来声明事件的类型:

- (NSArray<NSString *> *) supportedEvents
{
    return @[@"SDK_EVENT"];
}

随后使用下面的代码,来向 React Native 发送事件:

[self sendEventWithName:@"SDK_EVENT" body: @{@"injectedJS": javascript}];

React Native 编译问题

在这个过程中,还遇到编译的问题。

以下是编加编译的示例:

//:configuration = Debug
HEADER_SEARCH_PATHS = $(SRCROOT)/** $(SRCROOT)/node_modules/react-native/React $(SRCROOT)/../../../node_modules/react-native/React/** $(SRCROOT)/../react-native/React/**

//:configuration = Release
HEADER_SEARCH_PATHS = $(SRCROOT)/** $(SRCROOT)/node_modules/react-native/React $(SRCROOT)/../../../node_modules/react-native/React/** $(SRCROOT)/../react-native/React/**

//:completeSettings = some
HEADER_SEARCH_PATHS

还有:

//:configuration = Debug
FRAMEWORK_SEARCH_PATHS = $(SRCROOT)/../node_modules/react-native-xxx/ios/RNxxx/**

//:configuration = Release
FRAMEWORK_SEARCH_PATHS = $(SRCROOT)/../node_modules/react-native-xxx/ios/RNZxxx/**

//:completeSettings = some
FRAMEWORK_SEARCH_PATHS

除此,还需要在项目里手动添加库:

Link Binary with Libraries

添加: CrashReporter.framework, CoreTelephony.framework

以及相应的编译选项:

//:configuration = Debug
OTHER_LDFLAGS = -ObjC -lsqlite3 -lz -liconv -lstdc++ -lc++ -lstdc++.6

//:configuration = Release
OTHER_LDFLAGS = -ObjC -lsqlite3 -lz -liconv -lstdc++ -lc++ -lstdc++.6

//:completeSettings = some
OTHER_LDFLAGS

关于我

Github: @phodal     微博:@phodal     知乎:@phodal    

微信公众号(Phodal)

围观我的Github Idea墙, 也许,你会遇到心仪的项目

QQ技术交流群: 321689806

新书《前端架构:从入门到微前端》

《前端架构:从入门到微前端》是一本围绕前端架构的实施手册,从基础的架构规范,到如何设计前端架构,再到采用微前端架构拆分复杂的前端应用。本书通过系统地介绍前端架构世界的方方面面,来帮助前端工程师更好地进行系统设计。

前端架构包含以下五部分内容:

  • 设计:讲述了架构设计的模式,以及设计和制定前端工作流。
  • 基础:通过深入构建系统、单页面应用原理、前端知识体系等,来构建出完整的前端应用架构体系。
  • 实施:通过与代码结构的方式,介绍如何在企业级应用中实施组件化架构、设计系统和前后端分离架构。
  • 微前端:引入6种微前端的概念,以及如何划分、设计微前端应用,并展示了如何实现这6种微前端架构。
  • 演进:提出更新、迁移、重构、重写、重新架构等架构演进方式,来帮助开发人员更好地设计演进式架构。
comment

Feeds

RSS / Atom

最近文章

关于作者

Phodal Huang

Developer, Consultant, Writer, Designer

ThoughtWorks 高级咨询师

工程师 / 咨询师 / 作家 / 设计学徒

开源深度爱好者

出版有《前端架构:从入门到微前端》、《自己动手设计物联网》、《全栈应用开发:精益实践》

联系我: h@phodal.com

微信公众号: 与我沟通

标签