注册

被React Native插件狂虐2天之后,写下c++_share.so冲突处理心路历程

为了应对活体检测客户 react-native 端的支持,需要开发 react-native 插件供客户使用。关于react-native 插件开发具体可以参考react官网:



具体包含两部分



  1. ViewManager:包装原生的 view 供 react-native 的 js 部分使用
  2. NativeModule:提供原生的 api 能力供 react-native 的 js 部分调用

心路历程


参考着官方事例,插件代码很快就完成。开开心心把插件发布到 github 之后试用了一下就遇到了第一个问题


image.png


看错误很容易发现是 so 冲突了,也就是说 react-native 脚手架创建的项目原本就存在libc++_share.so,正好我们的活体检测 sdk 也存在 libc++_shared.so。冲突的解决方法也很简单,在 android 域中添加如下配置:


packagingOptions {
pickFirst 'lib/arm64-v8a/libc++_shared.so'
pickFirst 'lib/armeabi-v7a/libc++_shared.so'
pickFirst 'lib/x86/libc++_shared.so'
pickFirst 'lib/x86_64/libc++_shared.so'
}

这边顺便解释下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'
merge将匹配的文件合并添加到APK中merge 'lib/arm64-v8a/libc++_shared.so'

上述例子中处理的方式是遇到冲突取第一个libc++_shared.so。冲突解决之后继续运行,打开摄像头过一会儿就崩溃了,报错如下:


com.awesomeproject A/libc: Fatal signal 6 (SIGABRT), code -1 (SI_QUEUE) in tid 30755 (work), pid 30611 (.awesomeproject)

从报错信息来看只知道错误的地方在jni部分,具体在什么位置?哪行代码?一概不知。从现象来看大致能猜到错误的入口,于是逐行代码屏蔽去试,最后定位到报错的代码竟然是:


std::cout << "src: (" << h << ", " << w << ")" << std::endl;

仅仅是简单的c++输出流,对功能本来没有影响。很好奇为什么会崩溃,查了好久一无所获。既然不影响功能就先删掉了这行代码,果然就不报错了,功能都能正常使用了,开开心心的交给测试回归。一切都是好好的,直到跑在arm64-v8a的设备上,出现了如下报错:


1e14141e-51c8-42e7-a5c7-440905742247.png


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


4a1a64b0-296d-4753-abc1-92da09d60cde.png


a4d4f827-ccea-4817-9175-e47458f1c917.png


可以明显的看到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冲突问题,同时也解决了上述问题。配置如下:


externalNativeBuild {
ndk {
abiFilters "armeabi-v7a", "arm64-v8a"
}
cmake {
cppFlags "-std=c++11 -frtti -fexceptions"
arguments "-DANDROID_STL=c++_shared" //shared改为static
}
}

可惜的是例子中的sdk不止一个库动态依赖于c++_shared.so,所以这条路也行不通。那么只能从react-native侧出发寻找方案。


方案一(推荐)


找出react-native这边的c++_shared.so是基于什么ndk版本打出来的,想办法把两端的ndk版本保持统一,问题也就迎刃而解了。


b2d4115d-0316-47c5-a14f-3dd5daf167f9.png


从react-native对应的android工程的蛛丝马迹中发现大概是基于ndk r20b打出来的。接下来就是改造sdk中c++_shared.so基于的ndk版本了。



  1. 基于ndk r20b版本重新编译opencv库
  2. 把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库的,详见对比:


bd504920-1855-445d-8f8f-cf4b6e4feabd.png


4a77bb53-f0ad-45b4-862c-2e264b88db9d.png


那么我们把react-native版本降级为0.59以下也能解决问题,降级步骤如下:



  1. 进入工程

cd Temple


  1. 指定版本

npm install --save .6


  1. 更新

react-native upgrade


  1. 一路替换文件

fdf99f54-b121-4321-8956-6e3bce7efb99.png


总结


Android开发会面临各种环境问题,遇到问题还是要从原理出发,理清问题发生的根源,这样问题就很好解决。



链接:https://juejin.cn/post/6976473129262514207

0 个评论

要回复文章请先登录注册