被React Native插件狂虐2天之后,写下c++_share.so冲突处理心路历程
前情提要
为了应对活体检测客户react-native端的支持,需要开发react-native插件供客户使用。关于react-native插件开发具体可以参考react官网:
https://reactnative.cn/docs/native-modules-android
https://reactnative.cn/docs/native-modules-ios
https://reactnative.cn/docs/native-components-android
https://reactnative.cn/docs/native-components-ios
具体包含两部分
- ViewManager:包装原生的view供react-native的js部分使用
- NativeModule:提供原生的api能力供react-native的js部分调用
心路历程
参考着官方事例,插件代码很快就完成。开开心心把插件发布到github之后试用了一下就遇到了第一个问题

看错误很容易发现是so冲突了,也就是说react-native脚手架创建的项目原本就存在libc++_share.so,正好我们的活体检测sdk也存在libc++_shared.so。冲突的解决方法也很简单,在android域中添加如下配置:
1 | |
这边顺便解释下packagingOptions中几个关键字的意思和作用
| 关键字 | 含义 | 实例 |
|---|---|---|
| doNotStrip | 可以设置某些动态库不被优化压缩 | doNotStrip ‘*/arm64-v8a/libc++_shared.so’ |
| pickFirst | 匹配到多个相同文件,只提取第一个 | pickFirst ‘lib/arm64-v8a/libc++_shared.so’ |
| exclude | 过滤掉某些文件或者目录不添加到APK中 | exclude ‘lib/arm64-v8a/libc++_shared.so’ |
| exclude | 将匹配的文件合并添加到APK中 | merge ‘lib/arm64-v8a/libc++_shared.so’ |
上述例子中处理的方式是遇到冲突取第一个libc++_shared.so。冲突解决之后继续运行,打开摄像头过一会儿就崩溃了,报错如下:
1 | |
仅仅是简单的c++输出流,对功能本来没有影响。很好奇为什么会崩溃,查了好久一无所获。既然不影响功能就先删掉了这行代码,果然就不报错了,功能都能正常使用了,开开心心的交给测试回归。一切都是好好的,直到跑在arm64-v8a的设备上,出现了如下报错:

这次有明显的报错信息,意思是当运行opencv_java3.so的时候缺少_sfp_handler_exception函数,这个函数实际上是在c++_shared.so库中的。奇怪的是原生代码运行在arm64-v8a的设备上是好的,那怎么跑在react-native环境就会缺少_sfp_handler_exception函数了呢?
直到我在原生用ndk20a编译代码报了同样的错误,才意识到一切问题的源头是pickFirst引起的。


可以明显的看到react-native和原生环境跑出来的apk包中c++_shared.so的大小是不同的。
也就是说pickFirst是存在安全隐患的,就拿这个例子来说,假如两个c++_shared.so是用不同版本的ndk打出来的,其实内部的库函数是不一样的,pickFirst贸然选择第一个必然导致另外的库不兼容。那么是不是可以用merge合并两个c++_shared.so,试了一下针对so merge失效了,只能是另辟蹊径。
如果我们的sdk只有一个库动态依赖于c++_shared.so,大可把c++_shared.so以静态库的方式打入,这样就不会有so冲突问题,同时也解决了上述问题。配置如下:
1 | |
可惜的是例子中的sdk不止一个库动态依赖于c++_shared.so,所以这条路也行不通。那么只能从react-native侧出发寻找方案。
方案一(推荐)
找出react-native这边的c++_shared.so是基于什么ndk版本打出来的,想办法把两端的ndk版本保持统一,问题也就迎刃而解了。

从react-native对应的android工程的蛛丝马迹中发现大概是基于ndk r20b打出来的。接下来就是改造sdk中c++_shared.so基于的ndk版本了。
- 基于ndk r20b版本重新编译opencv库
- 把opencv库连接到项目,基于ndk r20b版本重新编译alive_detected.so库
把编译好的sdk重新导入插件升级,运行之后果然所有的问题得以解决。
方案二
去除react-native中的c++_shared.so库,react-native并不是一开始就引入了c++_shared.so。从React Native版本升级中去查看c++_shared.so是哪个版本被引入的,可以发现0.59之前的版本是没有c++_shared.so库的,详见对比:


那么我们把react-native版本降级为0.59以下也能解决问题,降级步骤如下:
- 进入工程
1 | |
- 指定版本
1 | |
- 更新
1 | |
- 一路替换文件

总结
Android开发会面临各种环境问题,遇到问题还是要从原理出发,理清问题发生的根源,这样问题就很好解决。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!