桔妹导读:在 react-native 生态中,包含原生 android 代码的 library 项目,都是通过项目的根文件夹中的 android 文件夹或者 android 文件夹下的 app 文件夹作为 library module 提供外部依赖。mand-mobile-rn 通过 rnpm 接口实现了对 react-native library 项目多个 Android module 的 link 实现。

mand-mobile-rn 是滴滴金融 FE 团队开发的面向金融场景的 react-native 组件库。

使用 react-native 的小伙伴,一定对下面列出的 react-native library 项目比较熟悉,这些组件都是通过封装原生模块或源生 UI 组件提供 react-native 侧调用。

  • react-native-vector-icons

  • react-native-svg

  • react-native-linear-gradient

上述列出的项目在 package.json 依赖 yarn 之后,调用 react-native link 命令,会在 Android 侧依赖引入相应的 library module,提供 react-native 依赖 Android 源生模块或者组件。


rootProject.name = 'samples'include ':react-native-vector-icons'project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android')include ':react-native-svg'project(':react-native-svg').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-svg/android')include ':react-native-linear-gradient'project(':react-native-linear-gradient').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-linear-gradient/android')include ':app'

通过 link 这三个项目,可以看出都是添加 android 目录的相对路径到 settings.gradle 提供外部依赖。

在 react-native 生态中,包含原生 android 代码的 library 项目,都是通过项目的根文件夹中的 android 文件夹或者 android 文件夹下的 app 文件夹作为 library module 提供外部依赖。

在 mand-mobile-rn 中,分别对 ImagePicker,RefreshControl,NumberKerboard 三个原生模块创建了三个原生 android module 提供外部依赖,有别于传统的 react-native library 项目,这三个 module 文件夹并没有放在 android 文件夹下(即使放在 android 文件夹下,也无法同时 link 这个三个module)。


.├── android├── build├── docs├── gradle├── ios├── samples├── scripts├── sites├── src│   ├── _styles│   ├── _utils│   ├── assets│   ├── components│   └── natives│       ├── Core│       ├── ImagePicker│       │   ├── android│       │   └── ios│       ├── NumberKeyboard│       │   ├── android│       │   └── ios│       └── RefreshControl│           ├── android│           └── ios├── template└── tests

mand-mobile-rn 是如何提供使用方集成的?通过下面的文章,来一步一步介绍。

1.

react-native link 命令是如何实现的?


在上文中写到 “即使放在 android 文件夹下,也无法同时 link 这个三个module” ,真的是这样吗?下面通过 react-native-cli 项目的源码来证实。

在 packages/cli/src/core/android/findAndroidAppFolder.js 文件中,定义了如下代码,用来寻找 link 的 library 项目中的 android 目录。


export default function findAndroidAppFolder(folder) {  const flat = 'android';  const nested = path.join('android', 'app');if (fs.existsSync(path.join(folder, nested))) {    return nested;  }if (fs.existsSync(path.join(folder, flat))) {    return flat;  }return null;}

这段代码可以看出,首先判断 android/app 文件夹是否存在,如果不存在,继续判断 android 文件夹是否存在。通过代码也就说明了,react-native link 命令,只会调用 library 项目根下的 android/app 或者 android 文件夹作为 library module,提供外部依赖。

2.

react-native-code-push 引发的思考


在集成 react-native-code-push 时,code push 会让你输入

”deployment key“,这和其他的 react-native library 项目的行为并不一致。在阅读 react-native-code-push 源码时, 发现 package.json 中有如下声明。


"rnpm": {    "android": {      "packageInstance": "new CodePush(${androidDeploymentKey}, getApplicationContext(), BuildConfig.DEBUG)"    },    "ios": {      "sharedLibraries": [        "libz"      ]    },    "params": [      {        "type": "input",        "name": "androidDeploymentKey",        "message": "What is your CodePush deployment key for Android (hit <ENTER> to ignore)"      }    ],    "commands": {      "postlink": "node node_modules/react-native-code-push/scripts/postlink/run"    }  }

这段声明,看起来就是让输入”deployment key“的原因。

通过 Google,可以搜索到 rnpm 项目(https://github.com/rnpm/rnpm ),在 react-native v0.27 版本时,合并进入 react-native-cli,但是 react-native-cli 文档中并没有详细介绍 rnpm,具体介绍和使用还要看 https://github.com/rnpm/rnpm 。

通过阅读文档,可知 commands prelink/postlink 可以指定脚本,用来 hook react-native link 命令执行前和执行后。

react-native-code-push 利用 postlink 来执行 node 脚本进行依赖代码的写入。

react-native-code-push 脚本代码:

https://github.com/Microsoft/react-native-code-push/blob/master/scripts/postlink/android/postlink.js

3.

mand-mobile-rn 实现


为了实现一次调用 link 命令,集成三个 android module,mand-mobile-rn 使用和 react-native-code-push 同样的原理,通过在 commands postlink 指定脚本,写入代码实现依赖。

注册一个 library 原生模块或原生组件到 react-native Android 工程中,大概需要如下三个步骤:

  1. 注册组件 module 到 ./android/settings.gradle

  2. 在 ./android/app/build.gradle 中声明依赖 module

  3. 在 ReactNativeHost.getPackages() 中注册组件的 package

▍applyPatch.js

上述的三个步骤,都要写入代码到文件中,因此定义一个 applyPatch.js 文件作为写入代码工具方法,代码如下:


var fs = require('fs')
function applyPatch(patch) {  if (!fs.existsSync(patch.path)) {    return Promise.reject(patch.noExistNotice)  }  var content = fs.readFileSync(patch.path, 'utf-8')console.log(`Writing ${patch.path}`)if (~content.indexOf(patch.patch)) {    console.log(patch.alreadyAddedNotice)  } else {    fs.writeFileSync(      patch.path,      content.replace(patch.pattern, (match) => `${match}${patch.patch}`),    )  }  return Promise.resolve()}
module.exports = applyPatch

读取目标文件内容,然后替换匹配的内容,最终再次写回到文件当中。

▍natives.json

因为每个模块的命名和路径肯定是不同的,同时为了方便 js 读取,定义 natives.json 文件来声明每个原生模块的全路径名和 module 位置。代码如下:


[  {    "moduleName": "mand-mobile-image-picker",    "packageName": "com.mandmobile.react.imagepicker.MDImagePickerPackage",    "modulePath": "src/natives/ImagePicker/android"  },  {    "moduleName": "mand-mobile-number-keyboard",    "packageName": "com.mandmobile.MDNumberKeyboardPackage",    "modulePath": "src/natives/NumberKeyboard/android"  },  {    "moduleName": "mand-mobile-refresh-control",    "packageName": "com.mandmobile.react.refreshcontrol.MDRefreshControlPackage",    "modulePath": "src/natives/RefreshControl/android"  }]

▍android/postlink.js

通过读取 natives.json 获取需要写入的组件,然后通过 Promise 顺序分别写入 settings.gralde,app/build.gradle,MainApplication。


module.exports = () => {  console.log('Running android postlink script')  if (!nativeModules || nativeModules.lenght == 0) {    return Promise.reject()  }return applyPatch(rootBuildGradlePatch())    .then(() => applyPatch(settingGradlePatch(nativeModules)))    .then(() => applyPatch(appBuildGradlePatch(nativeModules)))    .then(() => applyPatch(importApplicationPatch(nativeModules)))    .then(() => applyPatch(packagePatch(nativeModules)))}

▍settingGradlePatch.js

settingGradlePatch.js 负责组织写入数据,以符合 applyPatch.js 执行写入。代码如下:


function settingGradlePatch(mandMobilPath,natives) {  var patch = ''for (native of natives) {    patch += `include ':${native.moduleName}'project(':${native.moduleName}').projectDir = new File(rootProject.projectDir, '../${mandMobilPath}/${native.modulePath}')`  }return {    path: path.join('android', 'settings.gradle'),    pattern: /include\s* \'\:app\'/,    patch,    noExistNotice: `Couldn't find "settings.gradle" file. Please see Doc`,    alreadyAddedNotice: `"settings.gradle" is already linked`,  }}

appBuildGradlePatch,importApplicationPatch 等原理大同小异,都是通过正则匹配之后写入代码,不再重复。这里是源码入口

4.

总结


rnpm 极大方便了 react-native library 开发方,把复杂的依赖方式,写成脚本注入到代码中。通过 rnpm ,未来 mand-mobile-rn 可以扩展在 link 命令集成时,在命令行中选择是否依赖源生模块,以及依赖那个原生模块。

https://github.com/didi/mand-mobile-rn

本文作者▬游子聪
滴滴 | 高级软件开发工程师
来自于呼伦贝尔大兴安岭的程序员,专注于 Android 领域,平时也会写小程序,SpringBoot。
推荐阅读
▬更多推荐
▬
滴滴开源 / Open Source
Levin | AoE | Delta | Mpx | Booster | Chameleon | DDMQ | DroidAssist | Rdebug | Doraemonkit | Kemon | Mand Moblie | virtualApk | 获取更多项目技术干货 / Recommended article
WebPack 如何控制事件执行流 | Android 性能优化之 Activity 启动耗时分析 | HDFS 源码解读:HadoopRPC 实现细节的探究| 内容 

mand-mobile-rn 多 Android Module link 实现相关推荐

  1. 2020年React Native使用Ant Design Mobile RN组件

    Ant Design Mobile RN是一个很优秀的React Native 界面库,可以帮助我们简单方便的开发出漂亮的界面.我在基于0.63版本使用的过程中遇到一些小波折,比如字体无法正常,各种红 ...

  2. ReactNative开发——RN与android Native交互初探

    ReactNative开发--RN与android Native交互初探 环境 window10,reactnative 0.44版 RN调用android方法 1.导入NativeModules组件 ...

  3. RN 封装 Android原生组件

    RN 封装 Android原生组件 背景 当在React Native暂时未提供部分原生功能或者模块,我们需要复用部分原生代码时,比如复用一个原生方法,此时就需要将原生方法进行封装,暴露出一个接口来让 ...

  4. avast for android,avast! Mobile Security for Android 本地拒绝服务漏洞

    发布日期:2013-04-19 更新日期:2013-04-23 受影响系统: avast Mobile Security for Android 描述: ----------------------- ...

  5. RN与Android的交互

    Android端接口 RN端向Android侧通信可以通过Android侧的接口来实现. 实现接口 Android侧的接口都需要继承ReactContextBaseJavaModule类,如下: cl ...

  6. rn+与android+交互,React native 与Android原生交互方式(一)

    前言## 最近在做React Native开发的时候避免不了的需要原生模块和JS之间进行交互,其实RN和原生的通信大致分为两种情况:一种是Android主动向RN端发送事件和数据,另外一种是RN端被动 ...

  7. PhoneGap与Jquery Mobile结合开发android应用配置

    由于工作需要,用到phonegap与jquery moblie搭配,开发android应用程序. 这些技术自己之前也都没接触过,可以说是压根没听说过,真是感慨,在开发领域,技术日新月异,知识真是永远学 ...

  8. android module 引用libs里面的so文件_Android中的JNI开发,你了解多少?

    一,什么是任务及管理 任务是用户在执行某项工作时与之互动的一系列 Activity 的集合. 一.步骤,修改build.gradle,添加cmakelists,写JNI接口,写c++,这个是不是流水线 ...

  9. is not backed by gradle android,Module … is not backed by gradle

    可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试): 问题: I'm using IntelliJ IDEA Pro 13.1.2. Never used ...

  10. rn+android+sdk,RN与Android原生交互

    场景:在RN界面中需要调用原生的拍照和选择相册功能,将拍照或者选择的照片的路径回传给RN 步骤如下: 1.RN的界面跳转到原生Activity,并接收从原生回传的数据 import React, { ...

最新文章

  1. 应力循环次数60ant_循环超临界CO2对煤的孔隙结构和力学特性的影响研究
  2. 【Linux】7_存储管理基本分区
  3. Visual Studio IDE环境下利用模板创建和手动配置CUDA项目教程
  4. vue import组件的使用
  5. asp.net mvc 伪静态添加
  6. 安装及创建python虚拟环境
  7. 辛辛苦苦做了几天白忙活,错在哪里?
  8. 【转】C#实现SqlServer数据库的备份和还原
  9. 瀑布模型、快速原型模型、增量模型、螺旋模型、喷泉模型
  10. 平板电脑android 管理软件,应用宝HD下载V5.2.0.142 安卓版-Android平板电脑(aPad)专用西西软件下载...
  11. pe不认服务器硬盘,WINPE认不出硬盘或移动硬盘怎么办?
  12. docker使用国内加速器的正确姿势
  13. 控制系统中带宽的理解
  14. Unity网格变形插件的简单使用:以curve sculpt layered自由变换修改器为例
  15. HTML CSS 学习笔记
  16. css:层叠样式表(全)
  17. 华为 社招 C语言笔试,华为笔试C语言笔试题之3
  18. 应届毕业生到底有哪些优势呢
  19. html怎么设计为中文字体,CSS font-family中文字体设置方法
  20. CPU 的物理核与逻辑核

热门文章

  1. 不是谁多情,亦不是谁薄情
  2. 【Python军火库】Re基础入门:正则表达式
  3. 联想服务器CPU系列,联想推出采用第三代英特尔至强处理器的ThinkSystem SR860 V2服务器...
  4. C#编程,获取当前时间为一年的第几周的一种方法。
  5. ecshop 添加会员头像功能
  6. 请问蓝牙设备如何测试?
  7. 对接钉钉API语音功能相关文档
  8. Python连接多种数据库的方式
  9. web课程设计网页制作、基于HTML+CSS大学校园班级网页设计
  10. 华为血压表WATCH D测量血压的数据可靠吗