阅读更多

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 钱币类型,但关于货币类型有更好的格式化方法,这里只是简单的格式化,另外...

  • go 生成基于 graphql 服务器库.zip

    格奇尔根 首页 > 文件 > gqlgen是什么?gqlgen是一个 Go 库,用于轻松构建 GraphQL 服务器。gqlgen 基于 Schema 优先方法— 您可以使用 GraphQL Schema 定义语言来定义您的 API 。gqlgen 优先考虑类型安全— 您永远不应该看到map[string]interface{}这里。gqlgen 启用 Codegen — 我们生成无聊的部分,以便您可以专注于快速构建您的应用程序。还不太确定如何使用gqlgen?将gqlgen与其他 Go graphql实现进行比较快速启动初始化一个新的 go 模块mkdir examplecd examplego mod init example添加github.com/99designs/gqlgen到项目的 tools.goprintf '//go:build tools\npackage tools\nimport (_ "github.com/99designs/gqlgen"\n _ "github.com/99designs/gqlgen

  • 基于JAVA+SpringBoot+Vue+MySQL的社区物资交易互助平台 源码+数据库+论文(高分毕业设计).zip

    项目已获导师指导并通过的高分毕业设计项目,可作为课程设计和期末大作业,下载即用无需修改,项目完整确保可以运行。 包含:项目源码、数据库脚本、软件工具等,该项目可以作为毕设、课程设计使用,前后端代码都在里面。 该系统功能完善、界面美观、操作简单、功能齐全、管理便捷,具有很高的实际应用价值。 项目都经过严格调试,确保可以运行!可以放心下载 技术组成 语言:java 开发环境:idea 数据库:MySql8.0 部署环境:maven 数据库工具:navicat

  • 法研杯2021类案检索赛道三等奖方案源码+项目说明+数据.zip

    法研杯2021类案检索赛道三等奖方案源码+项目说明+数据.zip是一个专为计算机相关专业(如计科、信息安全、数据科学与大数据技术等)学生设计的宝贵学习资源。该压缩包包含了完整的项目源码、详细的项目说明文档以及用于训练和测试的数据集,旨在帮助参赛者深入理解并掌握类案检索的相关技术和方法。该项目通过实际案例,展示了如何运用自然语言处理和机器学习技术对法律案件进行智能检索和匹配。项目内容涵盖了从数据预处理、特征提取到模型训练和评估的全过程,为学习和研究类案检索技术提供了全面的参考。本项目不仅适合作为课程设计、期末大作业或毕设项目的参考,也是企业员工提升技能、进行实践操作的优质学习资料。通过实际操作和学习该项目,用户可以加深对类案检索技术的理解,并在实践中不断提升自己的技能水平。请注意,由于该资源包含完整的项目源码和数据集,下载和使用时请确保遵守相关法律法规和道德规范,尊重知识产权和隐私权。同时,建议用户在使用前仔细阅读项目说明文档,了解项目的整体架构和使用方法,以便更好地利用该资源进行学习和研究。

Global site tag (gtag.js) - Google Analytics