关于Android的.so文件所须要知道的
早期的Android系统差点儿仅仅支持ARMv5的CPU架构,你知道如今它支持多少种吗?7种。
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。
1.为什么你须要重点关注.so文件
假设项目中使用到了NDK,它将会生成.so文件,因此显然你已经在关注它了。假设仅仅是使用Java语言进行编码,你可能在想不须要关注.so文件了吧,由于Java是跨平台的。
但其实,即使你在项目中仅仅是使用Java语言,非常多情况下,你可能并没有意识到项目中依赖的函数库或者引擎库里面已经嵌入了.so文件,并依赖于不同的ABI。
比如,项目中使用RenderScript支持库,OpenCV。Unity,android-gif-drawable,SQLCipher等,你都已经在生成的APK文件里包括.so文件了。而你须要关注.so文件。
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文件的话。
2.App中可能出错的地方
处理.so文件时有一条简单却并不知名的重要法则。
你应该尽可能的提供专为每一个ABI优化过的.so文件,但要么全部支持。要么都不支持:你不应该混合着使用。你应该为每一个ABI文件夹提供相应的.so文件。
当一个应用安装在设备上。仅仅有该设备支持的CPU架构相应的.so文件会被安装。
在x86设备上,libs/x86文件夹中假设存在.so文件的话。会被安装,假设不存在,则会选择armeabi-v7a中的.so文件。假设也不存在,则选择armeabi文件夹中的.so文件(由于x86设备也支持armeabi-v7a和armeabi)。
3.其它地方也可能出错
当你引入一个.so文件时,不止影响到CPU架构。我从其它开发人员那里能够看到一系列常见的错误,当中最多的是"UnsatisfiedLinkError","dlopen: failed"以及其它类型的crash或者低下的性能:
使用android-21平台版本号编译的.so文件执行在android-15的设备上
使用NDK时。你可能会倾向于使用最新的编译平台,但其实这是错误的,由于NDK平台不是后向兼容的,而是前向兼容的。推荐使用app的minSdkVersion相应的编译平台。
这也意味着当你引入一个预编译好的.so文件时,你须要检查它被编译所用的平台版本号。
4.混合使用不同C++执行时编译的.so文件
.so文件能够依赖于不同的C++执行时,静态编译或者动态载入。混合使用不同版本号的C++执行时可能导致非常多奇怪的crash,是应该避免的。
作为一个经验法则。当仅仅有一个.so文件时,静态编译C++执行时是没问题的,否则当存在多个.so文件时。应该让全部的.so文件都动态链接同样的C++执行时。
这意味着当引入一个新的预编译.so文件,并且项目中还存在其它的.so文件时。我们须要首先确认新引入的.so文件使用的C++执行时是否和已经存在的.so文件一致。
5.没有为每一个支持的CPU架构提供相应的.so文件
这一点在前文已经说到了,但你应该真的特别注意它,由于它可能发生在根本没有意识到的情况下。
比如:你的app支持armeabi-v7a和x86架构。然后使用Android Studio新增了一个函数库依赖,这个函数库包括.so文件并支持很多其它的CPU架构,比如新增android-gif-drawable函数库:
compile ‘pl.droidsonroids.gif:android-gif-drawable:1.1.+’
公布我们的app后,会发现它在某些设备上会发生Crash,比如Galaxy S6,终于能够发现仅仅有64位文件夹下的.so文件被安装进手机。
解决方式:又一次编译我们的.so文件使其支持缺失的ABIs,或者设置
ndk.abiFilters
显示指定支持的ABIs。
最后一点: 假设你是一个SDK提供者,但提供的函数库不支持全部的ABIs。那你将会搞砸你的用户,由于他们能支持的ABIs必将仅仅能少于你提供的。
将.so文件放在错误的地方
我们往往非常easy对.so文件应该放在或者生成到哪里感到困惑,以下是一个总结:
- Android Studio工程放在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文件夹中。
仅仅提供armeabi架构的.so文件而忽略其它ABIs的
全部的x86/x86_64/armeabi-v7a/arm64-v8a设备都支持armeabi架构的.so文件。因此似乎移除其它ABIs的.so文件是一个降低APK大小的好技巧。但其实并非:这不仅仅影响到函数库的性能和兼容性。
x86设备能够非常好的执行ARM类型函数库。但并不保证100%不发生crash,特别是对旧设备。64位设备(arm64-v8a, x86_64, mips64)能够执行32位的函数库,可是以32位模式执行,在64位平台上执行32位版本号的ART和Android组件。将丢失专为64位优化过的性能(ART。webview,media等等)。
以降低APK包大小为由是一个错误的借口。由于你也能够选择在应用市场上传指定ABI版本号的APK,生成不同ABI版本号的APK能够在build.gradle中例如以下配置:
android {
...
splits {
abi {
enable true
reset()
include 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a' //select ABIs to build APKs for
universalApk true //generate an additional APK that contains all the ABIs
}
}
// map for the version code
project.ext.versionCodes = ['armeabi': 1, 'armeabi-v7a': 2, 'arm64-v8a': 3, 'mips': 5, 'mips64': 6, 'x86': 8, 'x86_64': 9]
android.applicationVariants.all { variant ->
// assign different version code for each output
variant.outputs.each { output ->
output.versionCodeOverride =
project.ext.versionCodes.get(output.getFilter(com.android.build.OutputFile.ABI), 0) * 1000000 + android.defaultConfig.versionCode
}
}
}
关于Android的.so文件所须要知道的的更多相关文章
- Linux内核:关于中断你须要知道的
1.中断处理程序与其它内核函数真正的差别在于,中断处理程序是被内核调用来对应中断的,而它们执行于中断上下文(原子上下文)中,在该上下文中执行的代码不可堵塞. 中断就是由硬件打断操作系统. 2.异常与中 ...
- 在Linux终端管理文件你要知道的11个命令
LS - 列表文件 ls命令列出目录中的文件. 默认情况下,使用ls列出当前目录下的文件. 2 你也可以列出文件递归-也就是说,列出所有文件在当前目录中的目录-使用ls -R.LS还可以列出在其他目录 ...
- 每一个JavaScript开发者都应该知道的10道面试题
JavaScript十分特别.而且差点儿在每一个大型应用中起着至关关键的数据.那么,究竟是什么使JavaScript显得与众不同,意义非凡? 这里有一些问题将帮助你了解其真正的奥妙所在: 1.你能 ...
- Android AndroidManifest 清单文件以及权限具体解释
每一个Android应用都须要一个名为AndroidManifest.xml的程序清单文件,这个清单文件名称是固定的而且放在每一个Android应用的根文件夹下.它定义了该应用对于Android系统来 ...
- android 上传文件
android对于上传文件,还是非常easy的,和java里面的上传都是一样的,基本上都是熟悉操作输出流和输入流!另一个特别重要的就是须要一些content-type这些參数的配置! 假设这些都弄好 ...
- Android 程序员必须知道的 53 个知识点
1. android 单实例运行方法 我们都知道 Android 平台没有任务管理器,而内部 App 维护者一个 Activity history stack 来实现窗口显示和销毁,对于常规从快捷方式 ...
- 为什么 Android Studio 工程文件夹占用空间这么大?我们来给它减减肥
偶然中发现Android Studio的工程文件夹比ADT Bundle的大很多.用Android Studio新建一个空工程,工程文件夹大小为30M,运行一次后大小为40M.同样用ADT Bundl ...
- android 打开各种文件(setDataAndType)转:
android 打开各种文件(setDataAndType) 博客分类: android-->非界面 android 打开各种文件 setDataAndType action动作 转自:htt ...
- 如何查看Android的Keystore文件的SHA1值
像使用百度地图api时候,一般需要获取keystore的SHA1值,这里就手把手教大家如何查看Android的keystore文件中的SHA1值. 第一步: 打开cmd,切换到keystore所在的文 ...
随机推荐
- 【Android 初学】13、Broadcast Receiver
Broadcast Receiver Android广播机制包括三个基本要素:广播(Broadcast) - 用于发送广播.广播接收器(BroadcastReceiver) - 用于接收广播:意图内容 ...
- Android开发之——编码规范
1. 前言 这份文档参考了 Google Java 编程风格规范和 Google 官方 Android 编码风格规范.该文档仅供参考,只要形成一个统一的风格,见量知其意就可. 2. 源文件基础 2.1 ...
- Shiro 学习应用(续)
在前面的文章中为大家介绍了 Shrio 的基础概念.可能比較笼统.没有深入到开发过程的一些问题.如今集中在本帖中归纳一下有关问题. FormAuthenticationFilter 表单过滤器 表单过 ...
- Android 利用TimerTask实现ImageView图片播放效果
在项目开发中,往往 要用到图片播放的效果.今天就用TimerTask和ImageView是实现简单的图片播放效果. 当中,TimerTask和Timer结合一起使用.主要是利用TimerTask的迭代 ...
- h5-news_index
aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAdcAAABuCAIAAAD6VM6fAAAUEUlEQVR4nO2dfXRTZZ7Hfx735Z/lr9 ...
- iOS判断一些权限是否被禁止
iOS中经常会遇到访问相册.相机.麦克疯.蓝牙.以及推送等权限,所以每次我们要使用这些权限是都要记得查看用户是否允许了,如果用户禁止了你的访问权限,你仍然去调取相册或者相机等,那么就会先出现下面的这个 ...
- ubuntu16.04下配置caffe无GPU
1. 安装依赖项 1 sudo apt-get install libprotobuf-dev libleveldb-dev libsnappy-dev libopencv-dev libhdf5- ...
- Linux uname 命令 打印系统信息
转自:https://www.jb51.net/LINUXjishu/417626.html 1.概述 打印系统信息 2.命令格式 uname [OPTION]... 3.常用命令参数 打印一些系统信 ...
- SQL语句之Group By
1. Group By 语句简介: Group By语句从英文的字面意义上理解就是“根据(by)一定的规则进行分组(Group)”.它的作用是通过一定的规则将一个数据集划分成若干个小的区域,然后针对若 ...
- 迭代器与函数Python学习(四)
1.1 迭代器: 迭代的工具 1.1.1 什么是迭代: 指的是一个重复的过程,每一次重复称为一次迭代,并且每一次重复的结果是下一次重复的初始值 while True: print('=====> ...