动态扩展组件
简介
目前机器人应用平台支持大部分常用的组件,但如果您想访问一个自定义的原生组件或需要扩展一个第三方开源的组件,机器人应用平台可以支持动态加载dex文件或so库,实现动态加载扩展的组件。此方法可为opk提供Android原生的大部分功能,甚至硬件级的功能(例如usb、蓝牙等),让opk的适用范围得到拓展。
此方法同样适用于把别人写好的react-native组件中的java代码部分集成到小豹机器人中进行使用!
支持版本
不同的机器人产品支持动态扩展组件的版本不同,具体各产品对应支持版本可参考下表:
机器人产品 | 开始支持的系统版本 |
---|---|
豹小秘 | 5.12 |
豹小秘DP | 4.18 |
豹小秘 mini | 5.13 |
功能限制
动态扩展组件是由机器人应用平台在底层加载,会有一定的侵入性,为了保护机器人应用平台的安全,不能随意的进行加载,会进行以下限制:
- 不能加载带有layout、drawable、string等资源的组件,加载后以上资源均无法获取。
- 不能和当前机器人应用平台已有的类或so库产生冲突,否则会加载失败。
- React Native组件桥接需要一个唯一的名称,该名称不能和当前机器人应用平台已有的重名,建议在名称中加入自身业务标识。
- 不能加载需要在AndroidManifest.xml中预埋内容的组件,预埋的内容会获取不到。
- 不能加载需要使用Assets资源的组件,对于文件资源建议存放于机器人应用的extraResource目录。
- 被加载的dex文件只能服务于一个opk程序,不能在多个opk之间共享
也即是可以把Java代码打包成dex,通过反射的形式进行加载运行。除java代码以外的Android内容无法被加载使用。
注意事项:
1、由于被加载的dex文件只能服务于一个opk程序,带有此功能的opk可能存在和现有代码冲突的问题。这类问题我们不负责处理。
2、动态dex桥接到native层的方法不支持重载,会崩溃。
3、文档中命令dexRelease对gradlew版本依赖5.4.1可用,6.x版本已经取消不确定能用,请尽量使用示例中的gradlew版本编译。
实现方式
这里我们以opk使用JAVA的国密SM2加解密库为例,说明如何实现这个功能:
1. 准备原生组件或三方开源组件
新建一个空的React Native工程,在android目录下新建一个lib module,可以将我们的原生组件代码或三方开源组件放置到lib mdoule中。
在国密sm2加密库项目中,我们直接新建了一个module的项目,项目结构如下:
2. 封装NativeModule及ReactPackage
React Native访问原生模块,需要将原生模块封装成一个NativeModule,并通过ReactPackage进行注册,具体实现方式可参考React Native官方文档。
文档地址:https://reactnative.dev/docs/native-modules-android
在Sm24jkxwPackage.java中按照要求声明给js调用的java module。
并在Sm24jkxwModule中输出我们需要的加解密接口(这个时候调用了国密SM2加解密算法)
3. 调用so文件
so库在opk使用是通过js调用java调用c,而不是js直接调用c,所以同样需要在Android项目中进行封装,将机器人支持的so库版本放入项目的libs下
local.properties引入ndk路径,build.gradle设置源码目录
调用so库需要向so库提供方要到so库原工具类以及包名,注意放工具类的包名必须与生成so库时工具类的包名一致
so库调用具体也可以百度
最后在NativeModule里面去调用工具类里的so库方法
4. 生成dex文件
原生组件模块最终是以dex文件的形式集成到机器人应用中,开发完成后,可通过以下命令令生成dex文件:
gradlew dexRelease
最终生成的dex文件,存储在{ libmodule目录 }/build/intermediates/dex/release/out目录下。
在国密sm2加密算法项目中,由于是直接建立的lib项目,所以执行的是./gradlew build命令进行jar包的打包,打包完在build/intermediates/packaged-classes/release/中会有一个叫classes.jar的文件。如果使用JD-gui查看这个jar包,会发现它打包了我们所需要的所有代码。
如果原生模块中引入了三方jar包,也需要将引入的jar包打包成dex文件,以fastjson为例:
dx --dex --output=fastjson.dex ./fastjson-1.2.61.jar
dx是Android官方SDK里提供的工具,具体使用方式可参考Android官方文档。
在这里我们直接把国密SM2算法源代码进行了删减硬写入了项目代码中,所以就不需要额外将SM2算法的jar打包成dex了。但我们需要把刚才的classes.jar打包为dex。
5. 集成到机器人应用
在机器人应用的extraResource目录下新建libs目录,并在libs目录下新建一个原生组件目录:
extraResource └── libs └── jiami ├── config.json ├── dexlib │ └── sm24jkxw.dex └── jnilib └── libaddLib.so
JavaCopy
jiami就是我们的原生组件名称,其下又包含两个目录及一个配置文件:
- dexlib dex文件存放目录,这里是刚才那个classes.jar生成的sm24jkxw.dex
- jnilib 当前原生模块依赖的so库存放目录(机器人支持armeabi-v7a版本so库),这里放的是要调用的libaddLib.so库
- config.json 用于配置ReactPackage类名称
新建config.json文件,配置ReactPackage,格式如下:
{ "packageClassNames": [ "com.ainirobot.sm24jkxw.Sm24jkxwPackage" ] }
在这里的packageClassNames是dex中需要反射加载的
6. 在机器人应用中调用原生模块
将dex集成到机器人应用中后,在机器人应用中就可以通过NativeModules获取我们封装的NativeModule,然后直接调用原生模块:
import { NativeModules } from 'react-native';const Sm24 = NativeModules.Sm24jkxwModule;
国密SM2加解密库的取名是Sm24jkxwModule,所以引用也是使用的这个名字。这里我们可以看到项目中加入了”4jkxw”,这是这个整体项目的标签,加入它以防止module冲突造成机器加载了这个dex之后无法正常运行。
NativeModule的调用为React Native框架支持,具体可参考React Native官方文档。
7. 机器人加载原生模块。
在orionos-sh debug模式下,是不会反射加载java代码的,我们需要通过orionos-sh run命令把带有dex模块的完整opk安装到机器中,再重启小豹程序即可加载。在启动小豹程序时,在logcat中填入筛选关键词“ReactPackageInDexUtil”可看到dex文件被加载的过程。
在国密SM加解密项目中,我们可以搜索到类似如下日志,说明库文件被加载了:
2021-04-27 19:35:32.272 19306-19306/? I/ReactPackageInDexUtil: parseClassNameAndSo OpkExtraPath /sdcard/robot/rndata/{opkid}/extra/libs/
由于在Sm24jkxwPackage中添加了Log.d(“Sm24jkxwPackage”,”Installed”);所以我们还能在logcat里看到Sm24jkxwPackage:Installed的日志
一旦完成dex文件的加载,之后就能使用orionos-sh debug进行正常的调试了,dex文件始终会被机器人加载并提供调用服务。