React native 官网上有关于 native module 的开发文档, ios 和 android(假设你已经熟悉了官方的这个2篇文档)。 文档给出了实现 RN native module 的 Api 说明和基本的例子。例子演示性的在 RN 的工程里直接添加了现实 native module 相关的源文件。按照这2篇文档去实现 android 和 ios 的 native module 后,其目录结构如下:

.

├── App.js

├── index.js

├── android

| ├── app

| | ├── src/com/***/

| | | ├── MainActivity.java

| | | ├── MainApplication.java

| | | ├── NativeModule.java // 继承 ReactContextBaseJavaModule 的类,

| | | | // 实现导出的给 js 的 api 接口

| | | └── NativeModulePackage.java // 继承 ReactPackage 的类,

| | | | // 注册 native module

| | └── ***

| └── ***

└── ios

├── projectNameFolder

| ├── AppDelegate.m

| ├── AppDelegate.h

| ├── NativeModule.h // 声明导出的给 js 的 api 接口,

| | // 声明该类实现 RCTBridgeModule 接口

| ├── NativeModule.m // 实现导出的给 js 的 api 接口

| └── ***

├── main.m

└── ***

我们可以看到 native module 的实现代码(NativeModule.java,NativeModulePackage.java, NativeModule.h, NativeModule.m)被直接插在了 RN 工程的项目里了。

对于演示场景,这样的实现没有问题,但是到了生产中就是另外一回事了。往往我们生产中的 native module 会更复杂,可能会涉及很多的源文件和第三方 SDK, 还要在不同的 RN 工程之间复用, 所以把 native module 独立工程化在生产中是必要的。

实际上我们对此并不陌生, 因为我们所使用的知名第三方 RN 插件均是脱离具体 RN 工程的独立模块,比如: react-native-navigation。

构建 Native Module 插件工程

本文的主要思路是将 native module 的 ios 原生代码做成一个 static Library 工程, android 原生代码做成一个 Android Library, 最后把 static Library 工程、Android Library 工程以及js 代码一起打包成 npm 包, 便于发布。目录结构如下:

.

├── android // Android Library, Android 的原生实现

| ├── app

| | ├── src/com/***/

| | | ├── NativeModule.java // 继承 ReactContextBaseJavaModule 的类,

| | | | // 实现导出的给 js 的 api 接口

| | | └── NativeModulePackage.java // 继承 ReactPackage 的类,

| | | | // 注册 native module

| | └── ***

| └── ***

└── ios // Ios static Library, ios 的原生实现

| ├── NativeModuleFolder

| | ├── NativeModule.h // 声明导出的给 js 的 api 接口,

| | | // 声明该类实现 RCTBridgeModule 接口

| | ├── NativeModule.m // 实现导出的给 js 的 api 接口

| | └── ***

├── demo // 示例的RN工程,同时也是开发的测试工程

| ├── android

| | └── ***

| ├── ios

| | └── ***

| ├── App.js

| └── index.js

├── index.js // 封装 js 的接口,便于使用。(可选的)

└── package.json // for release as npm package

示例项目: https://github.com/njleonzhang/play-rn-native-module

创建 demo 目录

demo 目录下就是一个普通的 rn 项目,rn 项目的创建过程参见 RN 文档, 创建命令:

react-native init demo

创建 Android Library

Android Studio 并不能直接创建一个 Android Library 的项目, 我通过以下的步骤曲线救国:

用 Android Studio 打开上面创建出来的 demo 目录下的 android 项目, 再创建模块。 步骤: Android studio -> File -> New -> New Module... -> Android Library. 完成后在 demo/android 的目录下会生成了我们想要 Android Library(示例项目 这个 Android Library 的名称为 react-toast-module)。

把 demo/android 下生成的这个 Android Library 模块剪贴到项目根目录的下 android 目录里。为了让 demo 项目能够正确的导入这个模块,我们需要修改一下 demo 目录下 Android 项目的配置文件: settings.gradle 和 demo/app/build.gradle:

// settings.gradle

include ':app', ':react-toast-module'

project(':react-toast-module').projectDir =

new File(rootProject.projectDir, '../../android')

// app/build.gradle

dependencies {

compile fileTree(dir: "libs", include: ["*.jar"])

compile "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"

compile "com.facebook.react:react-native:+" // From node_modules

compile project(':react-toast-module')

}

添加 react-native 作为这个模块的依赖

// ./android/build.gradle

dependencies {

compile fileTree(dir: 'libs', include: ['*.jar'])

compile 'com.android.support:appcompat-v7:26.1.0'

testCompile 'junit:junit:4.12'

androidTestCompile('com.android.support.test.espresso:espresso-core:3.0.2', {

exclude group: 'com.android.support', module: 'support-annotations'

})

compile "com.facebook.react:react-native:+" // From node_modules

}

按文档实现 native module.

以上关于 Android 原生开发部分的步骤,使用环境是: RN 版本 0.56.0, Android Studio 版本 3.1

创建 Ios static Library

Xcode 支持我们直接创建一个 Ios static Library 的工程,这就不需要像创建 Android Library 那么曲折啦,步骤如下:

在项目的 ios 目录下,用 XCode 创建一个 ios 工程,类型为静态库. 步骤: xcode -> File -> new -> Project -> Cocoa Touch Static Library.

按照 RN 文档 实现 native module,即实现 js 要调用的接口,并通过宏导出。

打开 demo/ios 下的示例 ios 项目 demo.xcodeproj,把上一步实现的 native module 静态库项目(示例代码里的./ios/Print/Print.xcodeproj 和 ./ios/SwiftPrint/SwiftPrint.xcodeproj)添加作为 Demo 工程的 Libraries里。

把 native module 工程的目标静态库,引入到 RN 工程(本例中的demo工程)的链接过程中,步骤:demo Project -> targets -> demo -> Build Phases -> Link Binary with Libraries -> 添加上面创建的静态库,示例项目 中为(libPrint.a 和 libSwiftPrint.a).

使用 Swift 开发时的注意点

如果你和我一样不熟悉 OC 的 smallTalk 语系的代码,那么你可能会想使用 swift 作为开发语言,那么这里有一些注意事项:

你无法彻底摆脱 OC, 所以需要处理好 swift 和 OC 互调

OC 调 swift,就 import 项目名-Swift.h

Swift 调 OC, 在 项目名-Bridging-Header.h 文件里引入 Swift 用到的 OC 类的头文件

Swift 不支持宏,所以 Swift 实现的接口,最终还是要通过 OC 的宏来导入. 如果你封装第三方库,那个库很有可能是 OC 写的, 那么你就需要从 Swift 去调用 OC 了。

使用 Swift 实现的接口时,注意 @objc 的使用

需要在 @objc 里把函数的声明完整的写一遍:

@objc(test:callback:)

func test(str: NSString, callback: RCTResponseSenderBlock) {

NSLog("swift print\(str)")

callback([1, ["x": 1, "y": 2, "z": 3, "date": NSDate().timeIntervalSince1970]]);

}

上述方案实在是冗余, 根据 stackoverflow 上 James Wang 的回答, 可以通过在函数的第一个参数前加个 _ 来省略 @objc 里的函数的声明:

@objc

func test(_ str: NSString, callback: RCTResponseSenderBlock) {

print("swift print\(str)");

NSlog("swift print\(str)")

callback([1, ["x": 1, "y": 2, "z": 3, "date": NSDate().timeIntervalSince1970]]);

}

RN 项目(即 示例项目 里的 demo 项目) 里的 ios 工程需要有一个空的 swift 文件, 否则链接会出错.

这点官方文档里有详细的说明:

Important when making third party modules: Static libraries with Swift are only supported in Xcode 9 and later. In order for the Xcode project to build when you use Swift in the iOS static library you include in the module, your main app project must contain Swift code and a bridging header itself. If your app project does not contain any Swift code, a workaround can be a single empty .swift file and an empty bridging header.

总体上说,使用 swift 还是有点麻烦,语法上虽然看着舒服点,但是需要去摸索 Swift 语言,还要处理混用 OC 和 Swift 的一些奇葩的地方。So, 你怎么选呢?

以上关于 ios 原生开发部分的步骤,使用环境是: RN 版本 0.56.0, Xcode 版本 9.4.1

index.js 和 package.json

示例项目 中, 我并没有写 index.js 和 package.json.

我们可以看下插件使用时候的代码,然后脑补一下:

// demo/App.js

import {Platform, StyleSheet, Text, View, NativeModules} from 'react-native';

***

if (Platform.OS === 'ios') {

NativeModules.Print.test1('fuck', (p1, p2) => {

console.log(p1, p2)

})

NativeModules.SwiftPrint.test('you', (p1, p2) => {

console.log(p1, p2)

})

console.log(NativeModules.Print.firstDayOfTheWeek, NativeModules.SwiftPrint.firstDayOfTheWeek)

} else {

console.log(NativeModules.Toast)

NativeModules.Toast.show('hello', NativeModules.Toast.LONG)

}

每次使用自定义的接口的时候,都要通过 NativeModules 的变量来访问,我们通过在 index.js 里创建一些辅助函数来简化一下吧:

// index.js

import { NativeModules } from 'react-native';

export let { Print, SwiftPrint, Toast } = NativeModules

简化使用:

// package.json 里设置 main 属性为 index.js

import { Print, SwiftPrint, Toast } from 'our-rn-native-module-package-name'

***

if (Platform.OS === 'ios') {

Print.test1('fuck', (p1, p2) => {

console.log(p1, p2)

})

SwiftPrint.test('you', (p1, p2) => {

console.log(p1, p2)

})

console.log(Print.firstDayOfTheWeek, SwiftPrint.firstDayOfTheWeek)

} else {

console.log(Toast)

Toast.show('hello', Toast.LONG)

}

当然我们的示例插件很简单,这个 index.js 可有可无, 但是对于复杂的插件来说,index.js 会有更多的帮助方法,工具类的封装和是插件启动的一系列代码。

使用插件

RN 插件发布后,一般都要写一段长长的说明文档,来告诉用户怎么去在 ios 和 android 项目里链接插件的原生代码。 比如react-native-navigation 的光配置部分就要3个章节:installation-ios、installation-android 和 Usage. Ios 配置一篇,android 配置一篇,js 的基本使用一篇.

非要搞得这么复杂么?原生部分的配置不能自动化么?不能不说这些原生部分的手动配置正说明这 RN 的不成熟,也是阻碍 RN 了发展。cordova 也有 native plugin,其原理和 RN native module 基本一样,但是 cordova 插件大多都能自动配置,不需要手工干预。

当然在这一方面 react 社区的一些努力。 rnpm 被 merge 到了 react 代码中后,react-native link 为 native plugin 自动化配置提供了规范, 理论上只要插件的开发人员提供相应的配置脚本,基本所有的插件都能做到自动化配置,比如, 极光推送的 rn 插件,自动化配置做得就很棒。(虽然它文档写得很乱

android开发rn插件,RN native module 插件开发相关推荐

  1. Android开发桌面插件

    前提:公司应用需要开发插件,奈何我实在没有块的开发经验,查阅了一些资料,花了几天时间,完成了插件的开发. 开发桌面插件需要了解AppWidgetProvider,RemoteViewsService, ...

  2. android微信支付插件,React Native集成微信支付【Android】

    wechat.jpg 本次博客主要讲解主要在RN中集成微信支付以及被我踩平的坑,需要一定的原生集成经验! 一. 导入微信SDK 用Android Studio打开RN项目中的android部分,在ap ...

  3. android开发ui插件下载,TKUISDK-ANDROID

    更新记录 4.3.4(2021-04-29) 1.修改请求权限在进入教室后 需删除之前版本的请求权限代码,并添加 权限请求回调代码 详见文档进入教室 2.4.1.1 2 新增旁听生身份 3 新增大班课 ...

  4. Android开发——AS插件批量解决XML中的String/Color/Dimen硬编码

    1. 问题抛出 1.1 开发方面 对于日常开发中,每写一个"#333",都要手动的在当前xml与colors.xml中来回切换,查看是否已经定义过,如果定义过则拿过来复用,如果没有 ...

  5. android开发获取内存、Native内存和虚拟内存的方式

    获取各种内存的方法 1.获取内存使用率 public static double getMemoryUsageRate() {long runtimeMaxMemory = getRuntimeMax ...

  6. android开发农场插件,超级农场app下载 超级农场 for android v1.1.3 安卓版 下载-脚本之家...

    超级农场安卓版是一款实用的模拟经营农场手游,超级农场手游中,画面很魔性,玩法也新颖! 玩法不再是以前那种单一的合成,是通过投资各种农作物赚金币来升级小龙虾,感兴趣的朋友不要错过了. 游戏介绍 超级农场 ...

  7. [Android系列—] 1. Android 开发环境搭建与Hello World

    前言 开始之前先熟悉几个名词: SDK -- Software Development Kit, 软件开发工具包.这个词并不陌生, JDK,就是Jave Development Kit,同样对于And ...

  8. 怎样搭建Android开发平台(转)

    Android是基于Linux内核的软件平台和操作系统,是Google在2007年11月5日公布的手机系统平台,早期由Google开发,后由开放手机联盟(Open Handset Alliance)开 ...

  9. Android开发实战一之搭建开发环境-附测试实例(已亲测)

    使用JDK.Android SDK.Eclipse + ADT搭建开发环境      附测试实例     (已亲测) ----------------------------------------- ...

  10. 在Eclipse上搭建Android开发环境

    声明:转摘请注明http://blog.csdn.net/longming_xu/article/details/28241045 前言:为什么要写这么一篇文章?网上介绍Android开发环境搭建的文 ...

最新文章

  1. 【CodeForces】961 F. k-substrings 字符串哈希+二分
  2. yolov5训练_YoloV5模型训练实战教程:Kaggle全球小麦检测竞赛
  3. hashmap原理_想要彻底搞懂HashMap?你得恶补下HashMap原理
  4. fpga项目开发实例_深入浅出玩转FPGA书+视频教程:35课时+源码
  5. 打通NTFS权限 文件共享各取所需
  6. android ipc简单理解,Android IPC 机制【1】--简介
  7. bat 变量 文件内容第一行_VBA基础入门(38)FSO生成bat文件后执行的实例
  8. [SQL Server]用于压力测试和性能分析的两个支持实用工具[转]
  9. Bailian4144 畜栏保留问题【贪心】
  10. python简单笔试题_这十道经典Python笔试题,全做对算我输
  11. 计算机二级wpsoffice知识点,2017全国计算机等级考试一级WPS office考试大纲
  12. pandas 聚合 df.groupby.agg
  13. 请你预想一下量子计算机未来,直播,研究量子计算机的我被曝光了
  14. linux无线网卡速度慢,Linux如何解决英特尔无线网卡WiFi网速慢、WiFi蓝牙无法共存等问题...
  15. PS用橡皮檫檫除图形与背景颜色一样的方法
  16. Android 获取夜深模拟器中的文件获取不到
  17. joan sola_Joan Touzet在CouchDB和Apache方式上
  18. ubuntu安装armadillo说明
  19. 中英双语多语言外贸企业网站源码系统 - HanCMS - 安装部署教程
  20. CoAP协议学习笔记(一)

热门文章

  1. linux系统上查询ip地址归属
  2. 银行核心系统概念入门简介
  3. 三菱FX系列PLC-编程1
  4. 超硬核!小白读了这篇文章,就能在算法圈混了
  5. 【Python】基于VB、Python、PythonGUI的BMI计算器小程序
  6. 数据挖掘原理与算法(第二版)
  7. 新书推荐|Windows黑客编程技术详解
  8. 有备无患:避免文件丢失的可行方案
  9. Fluent批处理及.jou和.scm文件编写的相关操作
  10. 外卖侠使用教程加体验地址