赞
踩
- 原标题: The Perils of Loading Native Libraries on Android
- 原文地址: medium.com/keepsafe-en…
- 原文作者: Hilal Alsibai
- 原文发布时间:2015年11月7日
笔者在去年遇到一个问题:项目打包成apk
文件后安装正常,打包成aab
文件后,通过bundleTool
解析成apk
文件后MMKV
初始化崩溃,但是将aab
文件上传到googlePlay
后,解析成apk
文件下载安装又正常。
通过阅读本文,你可以了解到为什么会出现这个问题,以及这个问题的解决思路。
早在2012
年,KeepSafe初期,我们试图为我们的Android
应用程序实施加密方案。通过多次迭代和原型,我们通过利用JNI(Java 原生接口)的强大功能找到了一个最佳点。我们决定将接口写入我们在Java
中使用的加密库中,通过JNI
调用该库仅用于加密和解密的目的。我们选择了即时解决方案,尽可能减少对用户体验的影响。一旦我们对我们的解决方案感到满意,我们决定将其部署到我们的生产应用程序中。我们严格测试了我们的代码,并相信一切都会顺利进行,但是,事情最后超出了我们的控制。
UnsatisfiedLinkError
初现当我们在发布后焦急地更新崩溃报告时,我们开始注意到一个反复出现的错误。用户遇到了UnsatisfiedLinkError
,这意味着有两种可能:(1)我们调用的本机库不存在;(2)我们调用的本机方法不存在。由于第二种几乎总是通过编译和基本测试被捕获,此刻我们对用户的安装没有我们在APK
中提供的so
本地库这一事实感到困惑。
以下是一些日志文件:
java.lang.UnsatisfiedLinkError: Couldn’t load stlport_shared from loader dalvik.system.PathClassLoader[dexPath=/data/app/com.kii.safe-1.apk,libraryPath=/data/app-lib/com.kii.safe-1]: findLibrary returned null\ at java.lang.Runtime.loadLibrary(Runtime.java:365)\ at java.lang.System.loadLibrary(System.java:535)\ at com.kii.safe.Native.<clinit>(Native.java:16)\ … 63 more Caused by: java.lang.UnsatisfiedLinkError: Library stlport_shared not found\ at java.lang.Runtime.loadLibrary(Runtime.java:461)\ at java.lang.System.loadLibrary(System.java:557)\ at com.kii.safe.Native.<clinit>(Native.java:16)\ … 5 more Caused by: java.lang.UnsatisfiedLinkError: Cannot load library: get_lib_extents[760]: 1305 — /mnt/asec/com.kii.safe-1/lib/libstlport_shared.so is not a valid ELF object\ at java.lang.Runtime.loadLibrary(Runtime.java:434)\ at java.lang.System.loadLibrary(System.java:554)\ at com.kii.safe.Native.<clinit>(Native.java:15) Caused by: java.lang.UnsatisfiedLinkError: Library cryptopp not found\ at java.lang.Runtime.loadLibrary(Runtime.java:461)\ at java.lang.System.loadLibrary(System.java:557)\ at com.kii.safe.Native.<clinit>(Native.java:17)
令人十分气愤的是
Caused by: java.lang.UnsatisfiedLinkError: Cannot load library: reloc_library[1286]: 1748 cannot locate ‘쯰ҷЦf1Ϙ˗˞ք0Ⱉض夘Ϛ.͏闑㥁ج뭫ර⓻в^ӎ3c`+W#Ҽ?-Bַˌ֕꼠’…\
at java.lang.Runtime.loadLibrary(Runtime.java:370)\
at java.lang.System.loadLibrary(System.java:535)\
at com.kii.safe.Native.<clinit>(Native.java:17)
Caused by: java.lang.UnsatisfiedLinkError: Cannot load library: reloc_library[1312]: 1327 cannot locate ‘Pܝ#ʭX Ϛߐܝ#ʭX Ϛߐܝ#ʭX Ϛߐܝ#ʭX Ϛߐܝ#ʭX Ϛߐܝ#ʭXߐܝ#ʭX Ϛߐܝ#ʭX Ϛߐܝ#ʭX Ϛߐܝ#ʭX Ϛߐܝ#ʭX Ϛߐܝ#ʭX Ϛߐܝ#ʭX Ϛߐܝ#ʭX Ϛߐܝ#ʭX Ϛߐܝ#ʭX Ϛߐܝ#ʭX Ϛߐܝ#ʭX Ϛߐܝ#ʭX Ϛߐܝ#ʭX Ϛߐܝ#ʭX Ϛߐܝ#ʭX Ϛߐܝ#ʭX Ϛߐܝ#ʭX Ϛߐܝ#ʭX Ϛߐܝ#ʭX Ϛߐܝ#ʭX Ϛߐܝ#ʭX Ϛߐܝ#ʭX Ϛߐܝ#ʭX Ϛߐܝ#ʭX Ϛߐܝ#ʭX Ϛߐܝ#ʭX Ϛߐܝ#ʭX Ϛߐܝ#ʭX Ϛߐܝ#ʭX Ϛߐܝ#ʭX Ϛߐܝ#ʭXߐܝ#ʭX Ϛߐܝ#ʭX Ϛߐܝ#ʭX Ϛߐܝ#ʭX Ϛߐܝ#ʭX Ϛߐܝ#ʭX Ϛߐܝ#ʭX Ϛߐܝ#\
at java.lang.Runtime.loadLibrary(Runtime.java:434)\
at java.lang.System.loadLibrary(System.java:554)\
at com.kii.safe.Native.<clinit>(Native.java:17)
没有明确的模式说明哪个库有问题,因为似乎所有so
库都在抛出异常。它不是特定于Android
版本的,它不仅发生在特定设备上。此外,在某些情况下,一些本机库被正确加载,但不是全部。在这一点上,我们在互联网上疯狂地寻找答案或帮助,结果却空手而归。我们开始发布修补程序,其中大部分是推测性修补程序、额外的跟踪数据,以便我们更好地了解崩溃发生时的确切环境,以及专门检查预期安装位置中的本机库的一段代码。
此外,在某些情况下,一些本机库被正确加载,但不是全部。
我们收集到的信息显示本地库不存在。系统类或文件系统错误并不是什么奇怪的侥幸,但是该用户的设备看起来很正常。 我们有了以下几点思路:
当我们考虑到这个异常的可能原因时,我们开始认为也许人们只是没有空间来正确安装本机库,因此它从未安装过。在尝试加载库之前进行快速诊断检查很快就证明了这个想法是错误的。用户在他们的设备上有足够的空间来存放我们正在运送的库。
so
库未包含在更新中造成我们问题的第二个可能原因是Google Play
在向用户设备提供APK
时破坏了我们的APK
。在阅读了诸如此类的报告后,我们对这个想法有了一些支持,详细说明Google Play
应该向所有受问题影响的应用程序开发人员发出通知,该问题导致用户在更新后无法启动他们的应用程序,因为本地库安装不正确。唯一的问题是这份报告是在8
月发布的,而我们在几个月后才开始处理这个问题。我们也从未收到来自Google Play
的通知,提到他们有任何错误。当然,这是很难验证的。
由于我们无法在手头的10
多种不同设备上重现该问题,因此我们决定联系遇到问题的用户。一位用户慷慨地决定帮助我们,并指出该应用程序在最新更新之前运行良好。这里的问题是,用户注意到的应用程序版本是一个包含我们的加密代码和本机库的版本,这只是为了增加我们的困惑和总体难题。我们决定直接向用户提供一个APK
文件,我们在其中验证了所有本机库都存在于APK
文件中。用户安装了APK
,启动了应用程序,然后又直接遇到了同样的UnsatisfiedLinkError
异常。这证实了不是Google Play
的问题,问题在于Android
的PackageManager
安装过程。
由于我们发现问题出在安装过程中,因此我们决定复制安装过程中提取应用程序代码中的本机库的部分。幸运的是,您可以通过以下方式轻松获取设备上应用程序APK
文件的引用
Context.getApplicationInfo().sourceDir;
然后我们将使用它来将本机库提取到内部存储位置。由于APK
文件只是ZIP
文件,因此只需编写一些ZIP
提取代码即可。我们迅速实现了提取代码并发货,导致崩溃数量大幅下降!每天抛出的 UnsatisfiedLinkError
异常总数如图:
为了减少我们的APK
大小并确保我们的应用程序可以在所有可能的设备上运行,我们为x86
、Armv7
和 Arm
架构设计了我们的应用程序。每种风格只包含与其各自架构对应的本机库,因此完全有可能有人可以安装不是为他们的设备架构制作的APK
。
我们开始在崩溃中记录安装程序包名称,并很快发现,是的,用户正在从各种来源安装应用程序,并且每个新的UnsatisfiedLinkError
都来自手动安装的应用程序,用户错误地为他们的设备安装了错误的abi
架构。这是最后的“陷阱”,我们松了一口气,因为它有一个非常简单的解释。
ReLinker
我们决定将提取代码打包成一个大家都可以使用的小库。任何人都不应该经历我们所经历的调试过程,尤其是当它涉及到一个非常基本的Android
功能时,它不在App
开发人员的控制范围内。
使用ReLinker
就像替换标准一样简单
System.loadLibrary(“mylibrary”);
使用回调
ReLinker.loadLibrary(context, “mylibrary”)
在发布修复程序时,遇到此崩溃的人数持续达到约100,000
名用户。我们希望您会发现ReLinker
很有用,并且永远不会再随机遇到UnsatisfiedLinkerError
。
其实框架做的事情很简单,在我们需要加载so
的时候,找到apk
文件内指定位置的so
文件加载,并且优化了so
的ABI
适配,进而解决了UnsatisfiedLinkError
。不过这种偏底层的问题确实是我们平时不容易遇到的。
so
文件,可以查看ApkLibraryInstaller
SystemLibraryLoader
ithub.com/KeepSafe/ReLinker/blob/master/relinker/src/main/java/com/getkeepsafe/relinker/SystemLibraryLoader.java")
【Android 详细知识点思维脑图(技能树)】
其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。
虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。
这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司19年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。
由于篇幅有限,这里以图片的形式给大家展示一小部分。
如果你有需要的话,可以扫描下方二维码,免费获取Android学习PDF+架构视频+面试文档+源码笔记领取方式。
Android架构视频+BAT面试专题PDF+学习笔记
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。