早期的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文件所须要知道的的更多相关文章

  1. Linux内核:关于中断你须要知道的

    1.中断处理程序与其它内核函数真正的差别在于,中断处理程序是被内核调用来对应中断的,而它们执行于中断上下文(原子上下文)中,在该上下文中执行的代码不可堵塞. 中断就是由硬件打断操作系统. 2.异常与中 ...

  2. 在Linux终端管理文件你要知道的11个命令

    LS - 列表文件 ls命令列出目录中的文件. 默认情况下,使用ls列出当前目录下的文件. 2 你也可以列出文件递归-也就是说,列出所有文件在当前目录中的目录-使用ls -R.LS还可以列出在其他目录 ...

  3. 每一个JavaScript开发者都应该知道的10道面试题

    JavaScript十分特别.而且差点儿在每一个大型应用中起着至关关键的数据.那么,究竟是什么使JavaScript显得与众不同,意义非凡? 这里有一些问题将帮助你了解其真正的奥妙所在:   1.你能 ...

  4. Android AndroidManifest 清单文件以及权限具体解释

    每一个Android应用都须要一个名为AndroidManifest.xml的程序清单文件,这个清单文件名称是固定的而且放在每一个Android应用的根文件夹下.它定义了该应用对于Android系统来 ...

  5. android 上传文件

    android对于上传文件,还是非常easy的,和java里面的上传都是一样的,基本上都是熟悉操作输出流和输入流!另一个特别重要的就是须要一些content-type这些參数的配置!  假设这些都弄好 ...

  6. Android 程序员必须知道的 53 个知识点

    1. android 单实例运行方法 我们都知道 Android 平台没有任务管理器,而内部 App 维护者一个 Activity history stack 来实现窗口显示和销毁,对于常规从快捷方式 ...

  7. 为什么 Android Studio 工程文件夹占用空间这么大?我们来给它减减肥

    偶然中发现Android Studio的工程文件夹比ADT Bundle的大很多.用Android Studio新建一个空工程,工程文件夹大小为30M,运行一次后大小为40M.同样用ADT Bundl ...

  8. android 打开各种文件(setDataAndType)转:

    android 打开各种文件(setDataAndType) 博客分类: android-->非界面 android 打开各种文件 setDataAndType action动作  转自:htt ...

  9. 如何查看Android的Keystore文件的SHA1值

    像使用百度地图api时候,一般需要获取keystore的SHA1值,这里就手把手教大家如何查看Android的keystore文件中的SHA1值. 第一步: 打开cmd,切换到keystore所在的文 ...

随机推荐

  1. Exception in thread "main" java.lang.IllegalArgumentException: Illegal character in query at index 189......

    Exception in thread "main" java.lang.IllegalArgumentException: Illegal character in query ...

  2. centos cmake 升级

    本以为升级cmake很简单 下载了最新的(3.15),./configure 没问题 make的时候,提示 openssl.c: undefined symbol openssl, openssl-d ...

  3. ios中去除tableView的分割线

     self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;

  4. AVL数

    平衡二叉树(AVL树) AVL树是一种二叉搜索树,并且每个节点的左右子树高度之差最多为1.AVL树是第一个在最坏的情况下保证以O(logn)的时间进行搜索,插入和删除操作的数据结构,AVL树能在对数时 ...

  5. PHP 导出excel 数据量大时

    public function ceshiexcel1(){ set_time_limit(0); $filename = '病毒日志'; header('Content-Type: applicat ...

  6. PHP中的类函数和类对象

    1.class_exists()函数接受表示类的字符串,检查并返回布尔值.如果类存在,返回true,否则返回false: echo class_exists('Computer'); 2.get_cl ...

  7. Mac 如何寻找Mac自带的IDLE

    Mac 如何寻找Mac自带的IDLE 每次要打开IDLE时,需要如下动作:打开terminal --> 输入idle --> 回车,就自动打开IDLE了 图标如下: 选择在“Finder中 ...

  8. struts中请求数据自动封装

    实现原理 参数拦截器 第一:jsp表单数据填充到action中的属性 必须实现set方法,get方法可以不需要实现,jsp页面name名字保持一致 第二:jsp表单填充到action的对象的属性 js ...

  9. bzoj 3028: 食物 生成函数_麦克劳林展开

    不管怎么求似乎都不太好求,我们试试生成函数.这个东西好神奇.生成函数的精华是两个生成函数相乘,对应 $x^{i}$ 前的系数表示取 $i$ 个时的方案数. 有时候,我们会将函数按等比数列求和公式进行压 ...

  10. C编译时`true' undeclared (first use in this function)

    在编译C语言时有时会遇到这样的错误提示: 'true' undeclared (first use in this function) or `false' undeclared (first use ...