本篇文章已授权微信公众号 dasu_Android(大苏)独家发布
这次想来讲讲系统应用集成过程中遇到的一些坑,尤其是 so 文件相关的坑。
背景
埋这些坑的最初来源是由于测试人员在集成新终端设备时提了个 bug: app 在这个设备上无法启动。
随后抛来了一份日志,过滤了下,最重要的其实就一条,crash 日志:
java.lang.UnsatisfiedLinkError: No implementation found for long com.facebook.imagepipeline.memory.NativeMemoryChunk.nativeAllocate(int) (tried Java_com_facebook_imagepipeline_memory_NativeMemoryChunk_nativeAllocate and Java_com_facebook_imagepipeline_memory_NativeMemoryChunk_nativeAllocate__I)
app 使用了 fresco 图片库,最初猜想是不是因为 so 文件没有 push,因为我们的 app 是系统应用,集成的时候是直接将 apk push 到 system/app 下的,因为没有 install 过程,所以 so 文件得自己 push 到 system/lib 下。
但把机子拿过来一看,so 文件有在啊,尝试将其删掉,再运行,又报出了如下异常:
java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/system/app/xxxx.apk"],nativeLibraryDirectories=[/system/lib64/xxxx, /vendor/lib64, /system/lib64]]] couldn't find "libimagepipeline.so"
看了下日志,它是说,在 system/lib64 目录下没有找到 so 文件,奇怪,以前都是只集成到 system/lib 下就可以了啊,怎么这次多出了个 system/lib64,难道这个机子支持的 CPUABI 不一样?试着运行了下 getprop | get cpu
:
果然,这个机子支持的 CPUABI 多了个 arm64-v8a。
那这个机子既支持 arm64-v8a,又支持 armeabi-v7a,我怎么知道,我的 app 该将 so 文件集成在哪里,什么场景该放 system/lib 下,什么时候该集成到 system/lib64 中?还是说,两个地方都放?
应该不至于两个目录都得集成,因为三方应用安装时,从 apk 包中也只会解压一份 so 文件而已,并不会将 lib 下所有 abi 架构的 so 文件都解压。
后来,试着查找相关资料,发现可以在 data/system/packages.xml 文件中找到自己 app 的相关配置信息,这里有明确指出该去哪里加载 so 文件,以及 app 所运行的 CPU 架构,所以我们可以运行如下命令:
cat /data/system/packages.xml | grep {你自己app的包名}
后来有些疑惑,这里的 primaryCpuAbi 属性值,系统是如何确定的,因为遇到过,明明这次的值是 armeabi-v7a,但当重启之后,有时候居然变成 arm64-v8a 了,所以就又去查找了相关资料,发现,这个值确定的流程蛮复杂的,影响因素也很多。
那么,就没有办法根据某些条件确定某个场景来确定 so 文件是该放 system/lib,还是 system/lib64 了,只能两个都集成了。于是乎,尝试着直接将 system/lib 下的 so 文件拷贝了一份到 system/lib64,结果发现运行报了如下异常:
java.lang.UnsatisfiedLinkError: dlopen failed: "libimagepipeline.so" is 32-bit instead of 64-bit
哎,想当然了,不同 CPU 架构的 so 文件肯定不一样,哪里可以直接将 armeabi-v7a 的 so 文件放到 system/lib64 里。因此,重新编译、打包了一份 arm64-v8a 架构的 so 文件,集成到 system/lib64 下,再运行,搞定。
但你以为事情到这里就结束了吗?年轻人,too yang.
由于以前 app 合作的机子,都只有 armabi-v7a 的,所以集成方式就一种,只需要集成到 system/lib 下就可以了,但由于新合作的机子有 arm64-v8a 的了,那么此时就需要修改以前的集成方式,分别将对应的 so 文件集成到对应的 system/lib 和 system/lib64 目录下。
但运维人员表示说,他不懂这些,他怎么判断说,什么时候该用旧的集成方式,什么时候用新的集成方式。我跟他说,你需要先执行 getprop | grep cpu
命令,查看当前机子支持的 CPUABI,然后再来决定你如何集成。但运维又说,这好复杂,能否有方法就统一一种集成方式,不必分场景考虑。
emmm,你们都是老大,你们说了算。只能又去瞎搞了,这次去开源库的 issue 里尝试寻找了下,结果发现,哈哈哈,原来这么多人碰到过这个问题:
要相信,你绝对不是第一个遇到问题的人。是吧,这么多人都来这里提问了,开源库的负责人肯定给出解决方案了,所以接下去继续在这些 issues 里过滤一下,找出那些跟你一样的问题就可以了。如下面这篇:
java.lang.UnsatisfiedLinkError #1552
官方人员已经说了,可以尝试使用 Relinker 或 SoLoader 来解决。
最后,我选择了 ReLinker,发现它的源码并不多,直接将所有源码拷贝到项目中,修改了源码中某个流程的逻辑,用于解决我自己这种场景下的 so 文件加载问题,搞定,具体在下面的埋坑一节讲述。
这整个过程中,遇到了一个又一个问题,一个又一个坑,解决这个异常,出现另一个异常,但整个过程梳理过来,也掌握了很多干货知识点,下面就用自己的理解,将这些相关的知识点梳理一下:
知识点
看完本篇,你能了解到哪些知识点呢,如下:
P1:了解系统应用集成方式,大概清楚 apk 的 install 过程都做了些什么。
P2:知道如何判断系统应用是否安装成功,懂得查看 data/system/packages.xml 文件来得知应用的基础信息,如 so 库地址,primaryCpuAbi 等。
P3:掌握 System.load() 和 System.loadlibrary() 的区别。
P4:清楚系统寻找 so 文件的大体流程,知道系统什么时候会去 system/lib 下加载 so 文件,什么时候去 system/lib64。
P5:了解 ReLinker 和 SoLoder 库的用途和大体原理。
正文
ps: 由于接触尚浅,还看不懂源码,正文部分大多数是直接从各大神博客中梳理出的结论,再用以自己的理解表达出来,因为并没有结合源码来分析,因此给出的结论观点不保证百分百正确,如有错误,欢迎指点一下。
ps: 以下知识点梳理基于的设备系统 Android 5.1.1,api 22,不同系统的设备,也许过程会有些许差别。
1. install 过程
要了解 apk 的 install 过程都干了哪些事,