我进行了一些涉及OpenSSL的原生Android开发。
我使用Android NDK独立工具链针对armeabi
(32b)进行交叉编译。我交叉编译本机C库,将OpenSSL /本机库.so
文件复制到我的libs/
文件夹中,我的gradle以此方式引用了该文件:
sourceSets {
main {
jniLibs.srcDir(file("libs/"))
}
}
无论如何,最终结果是我的.apk
看起来像这样:
- > classes.dex
- > lib/
-> armeabi/
-> libcrypto.so
-> libssl.so
-> libmynativelibrary.so
- > res/ (...)
- > resources.arsc
- > META-INF/ (...)
- > kotlin/ (...)
- > AndroidManifest.xml
共享库是正确的32位ARM ELF文件。我一直在API级别24设备上使用此确切的APK,并取得了巨大的成功(Android 7.0+)。
问题:当我切换到API级别21设备(Android 5.1-,我怀疑我在Android 6.0上也会遇到同样的问题)时,程序在加载{{1 }}。
由于libmynativelibrary.so
是libcrypto.so
的依赖项,因此程序尝试加载它。实际上,它在API级别24+上可以正常工作,但在API级别23-上崩溃。这是因为加载的库不是我的libmynativelibrary.so
中的那个库,而是系统中的那个。 And such libraries seem to not be available below API level 24.
我的问题:如何明确告诉Android首先在.apk
文件中查找该库,而不是常规系统库目录?
谢谢。
答案 0 :(得分:1)
在Nogut之前,系统库未受到用户应用程序的保护。名称冲突是有问题的,它们导致Google为Android NDK的C ++共享运行时库发明了一个单独的命名空间。
OpenSSL库也被广泛使用,超出了您的控制范围。它们甚至可能在您没有机会加载自己的libssl之前被加载到您的进程中。
因此,最好的选择是将OpenSSL构建为静态库,并将libmynativelibrary.so静态链接到该库。这样,您便拥有了一个不依赖于其他程序的整体二进制文件。
如果您不能遵循本课程,则应使用名称乱码的OpenSSL库,例如libmyssl.so和libmycrypto.so。这可能有助于避免与系统库的简单名称冲突。
更好的是,遵循NDK的示例,并为您提供SSL API的唯一名称空间。
不要期望从ApplicationInfo.nativeLibraryDir的未压缩位置显式加载库将是一个可靠的解决方案:正如我之前所搜索的那样,系统库可能恰好在之前加载到了您的地址空间中。
请注意,在Lollipop之前,您也必须以适当的顺序手动加载所有非系统依赖项。
此外,新的NDK删除了 armeabi ,因此请考虑切换到 armeabi-v7a 。