关于Android的.so文件你所需要知道的 - 简书
写了243611字,被2183人关注,获得了1305个喜欢
关于Android的.so文件你所需要知道的
@author ASCE1885的
广而告之时间:我的新书《Android 高级进阶》( )在京东开始预售了,欢迎订购!
TB2MnqlXH1J.eBjSszcXXbFzVXa_!!.png-39kB
早期的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。
为什么你需要重点关注.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中的一种。
这个应用可以帮助我们理解手机上***的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文件的话。
App中可能出错的地方
处理.so文件时有一条简单却并不知名的重要法则。
你应该尽可能的提供专为每个ABI优化过的.so文件,但要么全部支持,要么都不支持:你不应该混合着使用。你应该为每个ABI目录提供对应的.so文件。
当一个应用***在设备上,只有该设备支持的CPU架构对应的.so文件会被***。在x86设备上,libs/x86目录中如果存在.so文件的话,会被***,如果不存在,则会选择armeabi-v7a中的.so文件,如果也不存在,则选择armeabi目录中的.so文件(因为x86设备也支持armeabi-v7a和armeabi)。
其他地方也可能出错
当你引入一个.so文件时,不止影响到CPU架构。我从其他开发者那里可以看到一系列常见的错误,其中最多的是"UnsatisfiedLinkError","dlopen: failed"以及其他类型的crash或者低下的性能:
使用android-21平台版本编译的.so文件运行在android-15的设备上
使用NDK时,你可能会倾向于使用最新的编译平台,但事实上这是错误的,因为NDK平台不是后向兼容的,而是前向兼容的。推荐使用app的minSdkVersion对应的编译平台。
这也意味着当你引入一个预编译好的.so文件时,你需要检查它被编译所用的平台版本。
混合使用不同C++运行时编译的.so文件
.so文件可以依赖于不同的C++运行时,静态编译或者动态加载。混合使用不同版本的C++运行时可能导致很多奇怪的crash,是应该避免的。作为一个经验法则,当只有一个.so文件时,静态编译C++运行时是没问题的,否则当存在多个.so文件时,应该让所有的.so文件都动态链接相同的C++运行时。
这意味着当引入一个新的预编译.so文件,而且项目中还存在其他的.so文件时,我们需要首先确认新引入的.so文件使用的C++运行时是否和已经存在的.so文件一致。
没有为每个支持的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文件放在错误的地方
我们往往很容易对.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中如下配置:
enable true
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开发的知识,教程,解析,前沿信息,都可以,欢迎大家投稿~
内容可搞笑,可逗比,另外欢迎申请管理员
? 24874人关注
Android老鸟给新人的建议、资源。
更优质的原创内容,欢迎关注技术公众号,微信搜索:“Open软件开发小组”或者“open_dev”
? 14721人关注
Android深入理解、基础详解及各种Library使用介绍。认真做技术,好好享受人生。。
? 5380人?注
~~攒钱买一杯张三疯欧式奶茶~~
选择支付方式:翻译可以如此简单_AndroidDeveloper_传送门
翻译可以如此简单
stormzhang
AndroidDeveloper
读完本文大概需要2分钟。经常有不少人问我「张哥,我英语很差怎么办?会不会对我之后的发展有很大影响?」对于这个问题我想说「会」,但是你可以改善,英语差没关系,实际工作中用到的英语跟你上学时候考试的英语完全不一样,实际工作中还是以解决问题为主,当然如果你想着肉身翻墙这就另说了。虽然你英语很差,甚至英语四级都没过,但是你只需要坚持阅读英文文档,坚持用英语关键字去 Google 搜索,慢慢的你的英语水平会有很大改善,之后甚至还会习惯阅读英文文档。在阅读的时候我们肯定会配合一些翻译工具,遇到不认识的单词多查查嘛,那么今天我就来给大家推荐几个翻译工具,真的堪称好用到爆!11. 有道翻译翻译工具很多,但是我觉得「有道翻译」还不错,有客户端,有在线翻译,最关键的是还有开放api,你甚至可以用这个api自己做个App练手用。地址:/22. Transit「有道翻译」虽然好用,但是我们在看文章遇到不认识的单词先要复制、粘贴,然后再触发翻译,不觉得这个过程很繁琐么?而有了 Transit,直接选中自动翻译,让划词翻译如此简单。Transit 是一个 Chrome 插件,众所周知,我最爱的浏览器是 Chrome,丰富的插件让你几乎可以抛弃很多客户端,而有了 Transit 这个插件,我抛弃了有道翻译客户端。插件地址(需要翻墙):/webstore/detail/transit/pfjipfdmbpbkcadkdpmacdcefoohagdc33. ECTranslation我大部分情况下阅读文档都是在Chrome里面的,所以 Transit 几乎满足了我大部分需求,但是对于我们Android开发来说,有些时候我们需要在 Android Studio 里阅读源码,然后看到注释的时候会有很多不认识的单词,这个时候又得需要复制、粘贴,然后翻译,如果有类似Transit的翻译插件该多好啊。那么从今天开始,这个愿望实现了,我司的一位同事利用有道开放api,写了一个AS的插件,从此直接在AS里就可以很方便的翻译了。截个图你们感受下:目前该项目已经完全开源在GitHub,想要学习如何做一个AS插件的不妨移步GitHub。地址:/Skykai521/ECTranslation使用方法也很简单:clone项目,获取 ECTranslation.jar.打开Android Studio,Preferences -> Plugins -> Install plugin from disk -> 选择 ECTranslation.jar ,***并重启Android Studio.选中代码,选择 Edit -> Translate 或者按下 command + I ,即可翻译。当然如果你觉得默认的快捷键很不方便,可以自己更改键盘快捷键甚至鼠标操作,比如我就把它改成了双击翻译,我觉得非常方便,具体这样更改:使用键盘快捷键触发Preferences -> Keymap -> 搜索Translate - > 右键 -> Add Keyboard Shortcut ,输入你想要的快捷键即可。使用鼠标触发Preferences -> Keymap -> 搜索Translate - > 右键 -> Add Mouse Shortcut. 设置你想触发的鼠标操作即可。一般设置左键双击就行了。好用的翻译工具有很多,没法保证这些都是最好的,但是我只给大家推荐我认为「最好用的」翻译工具,希望大家能够喜欢!昨天的文章「」留言区顶部的一位同学过生日,说想被顶上去,毕竟一年只有一次,这小小的愿望果断满足啊,这里感谢大家的配合,除此之外,我还答应他说广告点击率高给他发个红包庆祝,他以为我开玩笑呢。虽然点击率并不高,但是我还是让他加了我微信,给他发了个小红包庆祝下,哈哈,今天他高兴坏了。有些时候生活就该有点小惊喜!对了,关于Google I/O 2016的直播在Youtube上肯定有,但是没有字幕的,有些同学翻墙有困难的不妨到优酷上观看,不知道优酷上有没有字幕。我随手也报了个名,如果有弹幕就好玩了,嘿嘿。地址在这里:/live/play?id=201
觉得不错,分享给更多人看到
AndroidDeveloper 微信二维码
分享这篇文章
5月18日 21:09
AndroidDeveloper 最新文章
AndroidDeveloper 热门文章android读取properties配置文件
因为一些配置信息,多处用到的。且以后可能变更的,我想写个.prorperties配置文件给管理起来。
我把配置文件放在了assets文件夹下
appConfig.properties:
serverUrl=http://192.168.1.155/ap
操作的工具类:
MyProperUtil.java:
.smartcost.offer.
import java.io.InputS
import java.util.P
import android.content.C
* 读取properties配置文件
public class MyProperUtil {
private static Properties urlP
public static Properties getProperties(Context c){
Properties props = new Properties();
//方法一:通过activity中的context攻取setting.properties的FileInputStream
InputStream in = c.getAssets().open("appConfig.properties");
//方法二:通过class获取setting.properties的FileInputStream
//InputStream in = PropertiesUtill.class.getResourceAsStream("/assets/
setting.properties "));
props.load(in);
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
urlProps =
System.out.println(urlProps.getProperty("serverUrl"));
return urlP
properties = MyProperUtil.getProperties(getApplicationContext());
url = properties.getProperty("serverUrl");
Log.i("URL", url);
(window.slotbydup=window.slotbydup || []).push({
id: '2467140',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467141',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467142',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467143',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467148',
container: s,
size: '1000,90',
display: 'inlay-fix'