LLDB全称Low Level Debugger ,并不是低水平的调试器,而是轻量级的高性能调试器

每个操作系统都会为运行在该系统下的应用程序提供应用程序二进制接口(Application Binary Interface,ABI

1. 什么是CPU架构及ABI

Android系统目前支持以下七种不同的CPU架构:ARMv5,ARMv7 (从2010年起),x86 (从2011年起),MIPS (从2012年起),ARMv8,MIPS64和x86_64 (从2014年起),每一种都关联着一个相应的ABI。

应用程序二进制接口(Application Binary Interface)定义了二进制文件(尤其是.so文件)如何运行在相应的系统平台上,从使用的指令集、内存对齐到可用的系统函数库。在Android系统上,每一个CPU架构对应一个ABI:armeabi,armeabi-v7a,x86,mips,arm64-v8a,mips64,x86_64。

2. 为什么需要重点关注.so文件

如果项目中使用到了NDK,它将会生成.so文件,因此显然你已经在关注它了。如果只是使用Java语言进行编码,你可能在想不需要关注.so文件了吧,因为Java是跨平台的。但事实上,即使你在项目中只是使用Java语言,很多情况下,你可能并没有意识到项目中依赖的函数库或者引擎库里面已经嵌入了.so文件,并依赖于不同的ABI。

Android应用支持的ABI取决于APK中位于lib/ABI目录中的.so文件,其中ABI可能是上面说过的七种ABI中的一种。

Native Libs Monitor这个应用可以帮助我们理解手机上安装的APK用到了哪些.so文件,以及.so文件来源于哪些函数库或者框架。当然,我们也可以自己对APP反编译来获取这些信息,不过相对麻烦一些。

很多设备都支持多于一种的ABI,例如ARM64和x86设备也可以同时运行armeabi-v7a和armeabi的二进制包。但最好是针对特定平台提供相应平台的二进制包,这种情况下运行时就少了一个模拟层(例如x86设备上模拟arm的虚拟层),从而得到更好的性能(归功于最近的架构更新,例如硬件fpu,更多的寄存器,更好的向量化等)。

我们可以通过Build.SUPPORTED_ABIS得到根据偏好排序的设备支持的ABI列表。但你不应该从你的应用程序中读取它,因为Android包管理器安装APK时,会自动选择APK包中为对应系统ABI预编译好的.so文件,如果在对应的lib/ABI目录中存在.so文件的话。

3. .so文件应该放在什么地方

我们往往很容易对.so文件应该放在或者生成到哪里感到困惑,下面是一个总结:

Android Studio工程放在main/jniLibs/ABI目录中(当然也可以通过在build.gradle文件中的设置jniLibs.srcDir属性自己指定)
Eclipse工程放在libs/ABI目录中(这也是ndk-build命令默认生成.so文件的目录)
AAR压缩包中位于jni/ABI目录中(.so文件会自动包含到引用AAR压缩包的APK中)
最终APK文件中的lib/ABI目录中
通过PackageManager安装后,在小于Android 5.0的系统中,.so文件位于app的nativeLibraryPath目录中;在大于等于Android 5.0的系统中,.so文件位于app的nativeLibraryRootDir/CPU_ARCH目录中。

4. 安装Apk时PackageManagerService选择解压so文件的策略

在Android系统中,当我们安装Apk文件的时候,lib目录下的so文件会被解压App的原生库目录,一般来说是放到/data/data/package-name/lib目录下,而根据系统和CPU架构的不同,其拷贝策略也是不一样的,不正确地配置so文件,比如某些App使用第三方的so时,只配置了其中某一种CPU架构的so,可能会造成App在某些机型上的适配问题。

Android版本    so拷贝策略    策略问题
< 4.0    遍历Apk中文件,当Apk中lib目录下主abi子目录中有so文件存在时,则全部拷贝主abi子目录下的so;

只有当主abi子目录下没有so文件的时候才会拷贝次abi子目录下的so文件。    当so放置不当时,安装Apk时会导致拷贝不全。比如Apk的lib目录下存在armeabi/libx.so,armeabi/liby.so,armeabi-v7a/libx.so这3个so文件,那么在主abi为armeabi-v7a且系统版本小于4.0的手机上,Apk安装后,按照拷贝策略,只会拷贝主abi目录下的文件即armeabi-v7a/libx.so,当加载liby.so时就会报找不到so的异常。另外如果主abi目录不存在,这个策略会遍历2次Apk,效率偏低。
4.0-4.0.3    遍历Apk中所有文件,如果符合so文件的规则,且为主abi目录或者次abi目录下的so,就解压拷贝到相应目录。    存在同名so覆盖,比如一个App的armeabi和armeabi-v7a目录下都包含同名的so,那么就会发生覆盖现象,覆盖的先后顺序根据so文件对应ZipFileR0中的hash值而定,考虑这样一个例子,假设一个Apk同时有armeabi/libx.so和armeabi-v7a/libx.so,安装到主abi为armeabi-v7a的手机上,拷贝so时根据遍历顺序,存在一种可能即armeab-v7a/libx.so优先遍历并被拷贝,随后armeabi/libx.so被遍历拷贝,覆盖了前者。本来应该加载armeabi-v7a目录下的so,结果按照这个策略拷贝了armeabi目录下的so。
> 4.0.4    遍历Apk中文件,当遍历到有主abi目录的so时,拷贝并设置标记hasPrimaryAbi为真,以后遍历则只拷贝主abi目录下的so,当标记为假的时候,如果遍历的so的entry名包含次abi字符串,则拷贝该so。    经过实际测试,so放置不当时,安装Apk时存在so拷贝不全的情况。这个策略想解决的问题是在4.0~4.0.3系统中的so随意覆盖的问题,即如果有主abi目录的so则拷贝,如果主abi目录不存在这个so则拷贝次abi目录的so,但代码逻辑是根据ZipFileR0的遍历顺序来决定是否拷贝so,假设存在这样的Apk,lib目录下存在armeabi/libx.so, armeabi/liby.so, armeabi-v7a/libx.so这三个so文件,且hash的顺序为armeabi-v7a/libx.so在armeabi/liby.so之前,则Apk安装的时候liby.so根本不会被拷贝,因为按照拷贝策略,armeabi-v7a/libx.so会优先遍历到,由于它是主abi目录的so文件,所以标记被设置了,当遍历到armeabi/liby.so时,由于标记被设置为真,liby.so的拷贝就被忽略了,从而在加载liby.so的时候会报异常。
64位    分别处理32位和64位abi目录的so拷贝,abi由遍历Apk结果的所有so中符合bilist列表的最靠前的序号决定,然后拷贝该abi目录下的so文件。    策略假定每个abi目录下的so都放置完全的,这是和4.0以前版本一样的处理逻辑,存在遗漏拷贝so的可能。
5. 配置so的建议

针对Android 系统的这些拷贝策略的问题,我们给出了一些配置so的建议:

5.1 针对armeabi和armeabi-v7a两种ABI

方法1:由于armeabi-v7a指令集兼容armeabi指令集,所以如果损失一些应用的性能是可以接受的,同时不希望保留库的两份拷贝,可以移除armeabi-v7a目录和其下的库文件,只保留armeabi目录;比如Apk使用第三方的so只有armeabi这一种ABI时,可以考虑去掉Apk中lib目录下armeabi-v7a目录。

方法2:在armeabi和armeabi-v7a目录下各放入一份so。

5.2 针对x86

目前市面上的x86机型,为了兼容arm指令,基本都内置libhoudini模块,即二进制转码支持,该模块负责把ARM指令转换为x86指令,所以如果是出于Apk包大小的考虑,并且可以接受一些性能损失,可以选择删掉x86库目录,x86下配置的armeabi目录的so库一样可以正常加载使用。

5.3 针对64位ABI

如果App开发者打算支持64位,那么64位的so要放全,否则可以选择不单独编译64位的so,全部使用32位的so,64位机型默认支持32位so的加载。比如Apk使用第三方的so只有32位ABI的so,可以考虑去掉Apk中lib目录下的64位ABI子目录,保证Apk安装后正常使用。

5. Android Studio配置abiFilters

android {
    defaultConfig {
        ndk {
            abiFilters 'armeabi-v7a' //, 'armeabi', 'arm64-v8a', 'x86', 'x86_64', 'mips', 'mips64'
        }
    }
}
这句话的意思就是指定NDK需要兼容的架构,把除了armeabi-v7a以外的兼容包都过滤掉,只剩下一个armeabi-v7a的文件夹。

即使我们没有指定其他的兼容框架,也需要一个过滤。当我们接入多个第三方库时,很可能第三方库做了多个平台的兼容。譬如fresco就做了各个平台的兼容,所以它创建了各个兼容平台的目录。因为只要出现了这个目录,系统就只会在这个目录里找.so文件而不会遍历其他的目录,所以就出现了找不到.so文件的情况。

6. java.lang.UnsatisfiedLinkError

该错误类型较多,以下进行分类:

java.lang.UnsatisfiedLinkError : dlopen failed: library //dlopen打开失败
java.lang.UnsatisfiedLinkError :findLibrary returned null //找不到library
java.lang.UnsatisfiedLinkError : Native method not found //找不到对应函数
java.lang.UnsatisfiedLinkError :Cannot load library: load_library //无法load library
出现原因:

显然出现上述崩溃的根本原因是:

(1)so无法加载,可能是so不存在等原因

(2)so正常加载,但是没有找到相应的函数

针对第二个原因,显然相对来说很容易排查,而且在开发中,这样的函数调用必然会在编译时和debug模式下进行测试,所以这种原因产生的概率很小。

那么下面主要总结几类“so无法加载”而导致上述崩溃的几种原因:

6.1 生成的so本身缺陷

一个简单的例子:

crash堆栈:

java.lang.UnsatisfiedLinkError: Cannot load library: find_library(linker.cpp:889): "/data/data/com.netease.nis.apptestunit/app_lib/libdemo.so" failed to load previously
 at java.lang.Runtime.load(Runtime.java:340)
 at java.lang.System.load(System.java:521)
 at com.netease.nis.bugrpt.ReLinker.loadLibrary(ReLinker.java:76)
 at com.example.crash.MainActivity.onCreate(MainActivity.java:272)
 at android.app.Activity.performCreate(Activity.java:5220)
 at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1086)
 at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2193)
 at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2279)
 at android.app.ActivityThread.access$600(ActivityThread.java:142)
 at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1272)
 at android.os.Handler.dispatchMessage(Handler.java:99)
 at android.os.Looper.loop(Looper.java:137)
 at android.app.ActivityThread.main(ActivityThread.java:5105)
 at java.lang.reflect.Method.invokeNative(Native Method)
 at java.lang.reflect.Method.invoke(Method.java:511)
 at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
 at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
 at dalvik.system.NativeStart.main(Native Method)
解决方法:

查看原项目Application.mk,发现APP_STL := gnustl_shared。原方案使用的是共享库,这不一定都支持所有的机型,改用静态库gnustl_static问题解决。 
对应的在Android Studio中需要将共享库改用静态库gnustl_static。这一类关于so编译共享库问题,需要进行检查。

APP_STL 可用值
 
system  系统默认
stlport_static - 使用STLport作为静态库
stlport_shared - 使用STLport 作为共享库
gnustl_static  -  使用GNU libstdc++ 作为静态库
gnustl_shared -  使用GNU libstdc++ 作为共享库
上述例子只是一个简单的例子,可能在so编译生成时,由于没有考虑共享库的机型匹配等原因导致UnsatisfiedLinkError崩溃,其次是64位32位系统架构问题,也可能导致UnsatisfiedLinkError崩溃。

6.2 手机设备没有空间

在so正确生成情况下,会根据设置的支持so库框架生成对应的库。在Android系统中,当我们安装Apk文件的时候,lib目录下的so文件会被解压到App的原生库目录,一般来说是放到/data/data/package-name/lib目录下,当准备加载native层的so时,虽然在Apk中有对应的so文件,但是由于手机设备没有足够的空间加载该so,导致加载失败,产生上述崩溃。

6.3 so配置错误

倘若so正确生成,且手机空间充足,那么如上所述,在Android系统中,当我们安装Apk文件的时候,lib目录下的so文件会被解压到App的原生库目录,一般来说是放到/data/data/package-name/lib目录下。但是根据系统和CPU架构的不同,其拷贝策略也是不一样的。倘若不正确地配置了so文件,比如某些App使用第三方的so时,只配置了其中某一种CPU架构的so,可能会造成App在某些机型上的适配问题,产生上述崩溃。

6.4 Android的PackageManager安装问题

用户安装了与手机CPU架构不符的Apk安装包,或者App升级过程中因各种原因未正确释放so文件。这种问题可以使用ReLinker解决。

使用ReLinker十分简单,使用

ReLinker.loadLibrary(context, “mylibrary”)
代替标准的即可。

System.loadLibrary(“mylibrary”);

so库你应该知道的基础知识相关推荐

  1. 社区网格员计算机考试题库,网格员考试——计算机基础知识题库

    网格员考试--计算机基础知识题库 计算机应用基础知识题库 1.第一代电子数字计算机适应的程序设计语言为机器语言. 2.计算机系统由两大部分构成,它们就是硬件系统与软件系统. 3.计算机中存储容量的基本 ...

  2. 浙大远程计算机基础知识题库,浙大远程教育1.计算机基础知识题答卷.docx

    浙大远程教育1.计算机基础知识题答卷.docx 第1章 计算机基础知识单选题 这些题目必须做一遍,来自统考题库(期末考试题也多半出在这里),参考答案在另一个Word文档中(上传自己做的答案后才可以下载 ...

  3. threading库:Python线程的基础知识

    目录 前言 Thread对象 区分线程 守护线程 自定义线程 定时器线程 线程间传送信号 前言 前面的subprocess库主要讲解的是进程知识与进程间的交互.而进程有可以拥有多个线程,所以threa ...

  4. MySQL a库备份恢复为B库_MySQL数据库备份的基础知识_MySQL

    一.数据备份捷径 因为这个方法没有得到官方正式文档的验证,我们暂称为试验吧. 目的:备份hostA主机中一个MySQL数据库备份TestA,并恢复到到hostB机中 试验环境: 操作系统:WinNT4 ...

  5. 五分之四的不同表达式_省技能大赛题库(五) 滴定分析基础知识

    1.滴定分析中,若怀疑试剂在放置中失效可通过()的方法检验. A.仪器校正B.对照试验C.空白试验D.无合适方法2.分析测定中出现的下列情况,属于偶然误差的是(). A.滴定时所加试剂中含有微量的被测 ...

  6. 10个必知的网页设计术语计算机与网络,入学测试计算机与网络应用基础知识题库(公开题)教学文稿(10页)-原创力文档...

    入学测试题库一一计算机与网络应用基础知识 (公开题) .单项选择题(每题给出的四个选项中,只有一个是正确答案,请将正确选项前的字母填 在题后横线上) 1世界上第一台电子计算机是 年研制成功的. D . ...

  7. 合肥市直计算机知识pdf,事业单位计算机基础知识题库(全).pdf

    事业单位计算机基础知识题库(全) 事业单位计算机基础知识1000选择题 1 ( )是指专门为某一应用目的而编制的软件. A:系统软件 B:数据库管理系统 C:操作系统 D:应用软件 2红灯停,绿灯行反 ...

  8. 二级公共基础知识_计算机系统习题,二级公共基础知识新增章节

    计算机系统知识为二级公共基础知识2020版考试大纲[1]新增章节,以下是计算机系统的习题,选自教育部考试中心指定教材 习题 1.下对计算机中整数的表示法中,可以直接作加或运算的是 A)原码 B)反码 ...

  9. 华图教育计算机基础知识,公共基础知识备考:计算机辅助制造

    华图教育网整理了2019公共基础知识题库及答案.公共基础知识模拟题.公共基础知识练习题.公共基础知识复习资料等,希望帮助考生快速顺利的掌握2019公共基础知识考点. 一.计算机辅助制造的概述 计算机辅 ...

  10. 基础知识:篇4-make工具与Makefile文件概念

    说明:   本文章旨在总结备份.方便以后查询,由于是个人总结,如有不对,欢迎指正:另外,内容大部分来自网络.书籍.和各类手册,如若侵权请告知,马上删帖致歉.   QQ 群 号:513683159 [相 ...

最新文章

  1. c++语言文件流,C++ IO类、文件输入输出、string流详细讲解
  2. 第七章 前端开发——前端工程化(NPM、脚手架、前端环境搭建)
  3. Qt文档阅读笔记-Widgets Tutorial官方解析及实例
  4. php+高德地图webapi 高德jsapi 实现 当前位置与目标位置距离 并按照距离排序(坐标逆转换)...
  5. typora html代码无效,Typora优化-适合不懂CSS代码的小白
  6. 对tensorflow 的BatchNormalization的坑的理解与测试
  7. NLP学习—9.Resent网络详解
  8. Python被誉为神奇的“胶水语言”,到底神奇在哪?
  9. JavaSE基础——代码块、继承、方法重写和final关键字
  10. C++跨平台开发——SOCKET网络编程中实现客户端对聊
  11. bat批量修改文件后缀名
  12. php 判断国际手机号码格式,国际短信验证码接口_国际验证码接口_国际短信API接口_国际短信API文档_...
  13. ie11不兼容 html编辑器,ewebeditor编辑器已经不能兼容IE11
  14. python中shelf对象_shelve -- 用来持久化任意的Python对象
  15. 用数据告诉你,哪位导演是漫威影片中的票房收割机?
  16. Curator的使用
  17. 给语音识别文本加上标点符号
  18. MySQL的主从配置+SpringBoot的MySQL读写分离配置
  19. CMMI认证是什么?为什么这些IT类企业都在申请?
  20. oracle 数据类型是什么,oracle的数据类型有哪些?

热门文章

  1. 山僧不识英雄主,何必晓晓问姓名
  2. php调用和风天气api,推荐一个免费7天天气预报API服务:和风天气
  3. redmon:Redis监控管理Web工具
  4. 小程序生成自带参数的小程序码
  5. 免费天气API,免费天气接口,天气预报
  6. ppt怎么压缩,ppt压缩的技巧分享
  7. python编写年金终值函数_财码Python管理会计小实验—投融资管理之货币时间价值...
  8. 中国大学moocpython笔记_中国大学MOOC —— 学习笔记(二)
  9. 固态硬盘与机械硬盘是否应该一起使用
  10. 在线绘制网络拓扑图操作方法分享