阅读更多

1顶
2踩

移动开发
在2012年KeepSafe的创业初期,我们试图找到一种为Android应用加密的方案,通过多次迭代与原型设计,我们最终找到了最佳方案——利用JNI(Java本地接口)。我们决定将接口写入Java加密库中,完全通过JNI来调用加密库,以实现加密与解密。我们选用了即时解决的方案,以期将对用户体验的影响减到最小,并决定在方案通过后就部署到生产应用上。我们严格测试代码,确认一切顺利,直到事情超出了控制。

遇到可怕的“UnsatisfiedLinkError”错误

在版本发布、大家焦虑地刷新错误报告之际,我们开始注意到,有一个错误反复重现。用户遇到“UnsatisfiedLinkError”错误的意思是,要么A) 我们调用的本地库不存在;要么B) 我们调用的本地方法不存在。鉴于B) 可能性在编译与基础测试时一般都能被发现,我们立即困惑于这一事实:用户并未安装我们打包在APK内的本地库。

下面有一些我们碰到过的异常样例,从标准类型的:
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)

出问题的库没有明确的模式可循,似乎所有库都可能会抛出异常,并不拘于某个版本的Android OS,也不拘于特定型号的设备。此外在特定情况下,一些本地库加载正常,但并非所有库都能正常加载。我们在互联网上就这个问题疯狂地搜索,寻找答案或帮助,却一无所获。我们开始发布专门的修补程序,大多都是测试性修复补丁,并附有跟踪数据,以便让我们更好地了解故障出现时的具体环境,补丁中还包括一块代码,专门负责检查本地库是否安装在正确的位置上。

此外在特定情况下,一些本地库加载正常,但并非所有库都能正常加载。

结果发现,果然是本地库缺失所导致的。这些错误并不是系统类或文件系统所导致的偶然性奇特错误,而且用户的设备似乎也是正常的。

想法1—用户设备空间用尽

在猜测异常可能原因的时候,我们最开始想到的就是:也许用户设备的空间不足而导致本地库未能正确安装。在快速检测后,我们很快发现这个想法是错误的,用户设备空间足够安装我们打包的库。

想法2—本地库未包含在更新中

第二个看似合理的猜测:在Google Play提供给用户的版本中,我们的APK被破坏掉了。在阅读了类似的一些报告后,我们更加确信这一点:据说Google Play向所有受到影响的应用开发者发送了通知,让他们通知用户在升级后先不要运行应用,因为本地库安装错误。唯一的问题在于,这个报告出现在8月份,而我们遇到问题的时候已经是好几个月以后了,而且我们也从未收到Google Play为这类错误负责的通知。此外,这一点很难证实。

想法3—与真实用户直接联调

由于在手头的十几台各种类型的设备上都无法重现这一问题,我们决定找一个遇到这个问题的用户来联调。一位友好用户决定要帮助我们,他表示应用在最近一次更新前都能正常使用。而这里有一个问题,用户表示能正常运行的应用版本正是包含了我们加密代码与本地库的那个版本,这让我们更加困惑。我们决定直接提供给这名用户一个验证过的APK包,其中包含了所有的本地库。在他安装了APK包之后,再次运行应用,又遇到了同样的UnsatisfiedLinkError错误。这证明Google Play不是问题的根源,Android的PackageManager安装过程才是问题所在,在安装期间会发生某种错误,导致APK中的本地库无法被提取。

问题在于Android的PackageManager安装过程

得出解决方案

鉴于我们发现了问题是安装过程所导致的,我们决定将该部分的安装过程复制下来,并将本地库提取到应用代码中。幸运地是,通过以下代码就能很容易的获得应用的APK文件参考:
Context.getApplicationInfo().sourceDir;

并用来将本地库提取到内部存储中。由于APK文件只不过是ZIP文件,写个ZIP提取代码只是小事一桩。我们很快实现了代码提取和打包,大幅降低了报错率。



日均UnsatisfiedLinkError错误的总量

尽管这是件好事,我们还是发现了一些用户时不时遇到异常抛出,在此我们寻求了Google的帮助。幸运地是,他们给了我们建议:用户可以通过除了Play Store以外的其他渠道来安装我们的应用,并告诉我们了一个技巧:通过
Context.getPackageManager().getInstallerPackageName(packageName);

就可以得知是哪个package安装了我们的应用。

为了减少APK大小,并确保应用尽可能在所有设备上可用,我们有支持x86、Armv7和Arm架构的应用包。每种只包含相应架构的本地库,所以很有可能某个用户安装的APK包并非是支持相应设备架构的那种。

我们开始在崩溃中记录安装包的名称,并且很快发现,问题产生的确是因为用户从各种途径安装应用,新增的UnsatisfiedLinkError错误都是由于用户手动安装应用时,选择了不适用他们设备架构的应用所致。这是最终的“问题揭秘”,我们都放下心来,这个解释简单明了。

引入ReLinker

我们决定将提取的代码打包成一个人人适用的小型库。鉴于我们所经历过的这个debug过程,不应当再有人重蹈覆辙,尤其是其中还涉及到了超出应用开发者控制能力之外的Android基础功能。

使用ReLinker十分简单,就像用
ReLinker.loadLibrary(context, “mylibrary”)

来代替标准的
System.loadLibrary(“mylibrary”);

Github库中 你能找到更多ReLinker的相关内容。

问题解决

在发布修复补丁的时候,有约10万的独立用户持续不断地遇到这种崩溃性错误。我们希望ReLinker有用,不再让大家遇到UnsatisfiedLinkerError错误。

感谢

感谢谷歌的支持团队为我们指出正确的方向,还有他们在这个问题上对我们持续不断地支持!

我们的代码主要是基于Chromium类似的权宜方案。在Chromium的源代码中有一条注释,解释了Android package manager中的具体问题:
/**
* Try to load a native library using a workaround of
* http://b/13216167.
*
* Workaround for b/13216167 was adapted from code in
* https://googleplex-android-review.git.corp.google.com/#/c/433061
*
* More details about http://b/13216167:
* PackageManager may fail to update shared library.
*
* Native library directory in an updated package is a symbolic link
* to a directory in /data/app-lib/<package name>, for example:
* /data/data/com.android.chrome/lib -> /data/app-lib/com.android.chrome[-1].
* When updating the application, the PackageManager create a new directory,
* e.g., /data/app-lib/com.android.chrome-2, and remove the old symlink and
* recreate one to the new directory. However, on some devices (e.g. Sony Xperia),
* the symlink was updated, but fails to extract new native libraries from
* the new apk.
+
* We make the following changes to alleviate the issue:
* 1) name the native library with apk version code, e.g.,
* libchrome.1750.136.so, 1750.136 is Chrome version number;
* 2) first try to load the library using System.loadLibrary,
* if that failed due to the library file was not found,
* search the named library in a /data/data/com.android.chrome/app_lib
* directory. Because of change 1), each version has a different native
* library name, so avoid mistakenly using the old native library.
*
* If named library is not in /data/data/com.android.chrome/app_lib directory,
* extract native libraries from apk and cache in the directory.
*
* This function doesn’t throw UnsatisfiedLinkError, the caller needs to
* check the return value.
*/


原文地址:Medium
  • 大小: 11.8 KB
来自: CSDN
1
2
评论 共 0 条 请登录后发表评论

发表评论

您还没有登录,请您登录后再发表评论

相关推荐

  • C++程序设计练习(2) Online Judge

    输出 对于每一种情形,依次输出空格的个数、数码字符的个数、大写字母的个数、小写字母的个数,数据之间用逗号及空格字符分隔,然后换行。 15 态度决定一切 问题描述 将英文26个字母A~Z,或a~z对应到整数1~26,则...

  • js数字的货币格式表示法(每三位一个逗号分隔)

    要显示如下格式的数字,即数字的金钱表示法:小数部分保留两位小数,整数部分每隔三位使用一个“,”分隔 代码如下: //保留两位小数并且整数部分三位一个逗号分隔符的数字金钱标准表示法: //这里假设我们即不...

  • oracle 每3位加逗号,[DB][Oracle]Oracle格式化数字的方法(指定小数点位数,每3位加逗号)...

    1.12345---&gt;'1.1235' 最终实现如下: /* FM :除空格 9999999.0099:允许小数点左边最大正数为7位,小数点右边最少2位,最多4位,且在第5位进行四舍五入 */ Select TO_CHAR(123.0233, 'FM9999999.0099') FROM DUAL

  • js中 将数字格式 转化成金额格式,三个数字一个逗号格式

    } 还有一个是比较懒人的方法, 就是js又一个原生的方法 可以按照一定格式输出数字的格式 let a = 10000 console.log(a.toLocaleString()) // 输出 10,000 也满足小数的情况, 但有个尴尬的问题是 上面的这个 执行...

  • python数据结构和算法 时间复杂度分析 乱序单词检测 线性数据结构 栈stack 字符匹配 表达式求值 queue队列 链表 递归 动态规划 排序和搜索 树 图

    python数据结构和算法1 参考 本文github 计算机科学是解决问题的研究。... 列表、元组和字符串都是用Python有序集合构建的。 字典和集合是无序的数据集合。 类允许程序员实现抽象的数据类型。 程...

  • 7. JDK拍了拍你:字符串拼接一定记得用MessageFormat#format

    目录 ✍前言 版本约定 ✍正文 DateFormat:日期时间格式化 SimpleDateFormat NumberFormat:数字格式化 DecimalFormat 一、0和#的使用(最常见使用场景) 二、科学计数法E 三、分组分隔符, 四、百分号% 五、本地货币...

  • 手把手教你用R处理常见的数据清洗问题(附步骤解析、R语言代码)

    给定一个集合或赌博数据库(格式为逗号分隔或CSV文本文件),其中包括的数据如老虎机的位置、钱的面额、月份、日、年、机器类型、机器的年龄、促销、优惠券、天气和投币量(投币量是放入机器的钱币总额减去支付的...

  • 正则表达式收藏(三)之高级技巧

     想必各位大大在做文件查找的时侯都有使用过万用字符”*”,比如说想查找在Windows目录下所有的Word文件时,你可能就会用”*.doc”这样的方式来做查找,因为”*”所代表的是任意的字符。RE所做的就是类似这样的功能...

  • php的正则表达式完全手册

    只要认真去阅读这些资料,加上应用的时候进行一定的参考,掌握正则表达式不是问题。 索引  1._引子  2._正则表达式的历史  3._正则表达式定义  3.1_普通字符  3.2_非打印字符  3.3_特殊字符  3.4_限定符

  • Java NumberFormat数字、货币值和百分数等的格式化处理

    Java数字、货币值和百分数等的...如果我们用下列语句输出一个数 System.out.println(123456.789); 将会在Console看到输出 123456.789 那么如何得到123,456.789这种格式化的输出呢?这里就需要用到java.text.Format这个

  • 华为2017校招机试题

    命令字与参数间使用一个空格分隔,不同命令之间用逗号分隔。 1). 投币命令:命令格式:p 钱币面额,投币可以投入多次,只能投入1、2、5元面额的钱币。 2). 购买商品:命令格式:b 商品名称 一条购买命令仅能...

  • Python入门练习题附答案

    输入一个整数金额值,给出找钱的方案,假设人民币足够多,且优先使用面额大的钱币。

  • php正则表达式基本语法

    现在我们加上用来增加可读性的逗号(每隔三位), 我们可以这样表示: ^[0-9]{1,3}(,[0-9]{3})*(/.[0-9]{1,2})?$ 不要忘记加号 '+' 可以被乘号 '*' 替代如果你想允许空白字符串被输入话 (为什么?). 也不要忘记...

  • 上海大学2020年夏季程序实习OJ平台练习题目记录

    问题描述 给定非负整数n,计算n的阶乘尾部0的个数。 输入 输入数据有若干行,每行上有一个非负整数n,对应一种情形。 输出 对于每一种情形,直接输出结果、换行。 输入样例 8 16 30 输出样例 1 3 7 #include &lt;...

  • php正则匹配大全

    用好正则表达式往往会起到事半功倍的效果,以下是对PHP正则表达式的语法汇总和详细介绍,需要的朋友可以过来参考下。 首先,让我们看看两个特别的字符:'^' 和 ‘$' 他们是分别用来匹配字符串的开始和结束,一下...

  • CG系统编程练习题

    日历问题 成绩大排队 最少钱币数 写出来吧 字符串数字置换 到底买不买 选美比赛 数码管 挖掘机技术哪家强 多项式加法 恺撒Caesar密码 在霍格沃茨找零钱 拼写检查 蛇行矩阵 公交系统 部分A+B 数塔 ...

  • 计算机浮点数格式化表示方法,Format函数

    返回的是:this is 4,552.22 注意:一是,只表示到小数后两位 二是,即使小数没有被截断,它也不会也像整数部分一样有逗号来分开的 6)m 钱币类型,但关于货币类型有更好的格式化方法,这里只是简单的格式化,另外...

  • 基于springboot框架的毕业设计系统的开发(完整Java源码+数据库sql文件+项目文档+Java项目编程实战+编程练手好项目).zip

    在如今社会上,关于信息上面的处理,没有任何一个企业或者个人会忽视,如何让信息急速传递,并且归档储存查询,采用之前的纸张记录模式已经不符合当前使用要求了。所以,对学生毕业设计信息管理的提升,也为了对学生毕业设计信息进行更好的维护,毕业设计系统的出现就变得水到渠成不可缺少。通过对毕业设计系统的开发,不仅仅可以学以致用,让学到的知识变成成果出现,也强化了知识记忆,扩大了知识储备,是提升自我的一种很好的方法。通过具体的开发,对整个软件开发的过程熟练掌握,不论是前期的设计,还是后续的编码测试,都有了很深刻的认知。 毕业设计系统通过MySQL数据库与Spring Boot框架进行开发,毕业设计系统能够实现教师管理,公告类型管理,班级管理,课题信息管理,任务类型管理,选题申请管理,学院管理,课题任务管理,最终成绩管理,公告信息管理,学生管理等功能。 通过毕业设计系统对相关信息的处理,让信息处理变的更加的系统,更加的规范,这是一个必然的结果。已经处理好的信息,不管是用来查找,还是分析,在效率上都会成倍的提高,让计算机变得更加符合生产需要,变成人们不可缺少的一种信息处理工具,实现了绿色办公,节省社会资源

  • hegaojian_WanAndroid_1742851819.zip

    hegaojian_WanAndroid_1742851819.zip

  • 《基于YOLOv8的违章停车识别系统》(包含源码、完整数据集、可视化界面、部署教程)简单部署即可运行。功能完善、操作简单,适合毕设或课程设计.zip

    资源内项目源码是来自个人的毕业设计,代码都测试ok,包含源码、数据集、可视化页面和部署说明,可产生核心指标曲线图、混淆矩阵、F1分数曲线、精确率-召回率曲线、验证集预测结果、标签分布图。都是运行成功后才上传资源,毕设答辩评审绝对信服的保底85分以上,放心下载使用,拿来就能用。包含源码、数据集、可视化页面和部署说明一站式服务,拿来就能用的绝对好资源!!! 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、大作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.txt文件,仅供学习参考, 切勿用于商业用途。

Global site tag (gtag.js) - Google Analytics