android开发——.so文件相关知识点

1 篇文章 0 订阅

一、为什么你需要重点关注.so文件

  什么是so文件

  so是shared object的缩写,见名思义就是共享的对象,机器可以直接运行的二进制代码。大到操作系统,小到一个专用软件,都离不开so。参见https://en.wikipedia.org/wiki/Library_(computing)
so主要存在于Unix和Linux系统中。

  如果项目中使用到了NDK,它将会生成.so文件,因此显然你已经在关注它了。如果只是使用Java语言进行编码,你可能在想不需要关注.so文件了吧,因为Java是跨平台的。但事实上,即使你在项目中只是使用Java语言,项目中依赖的函数库或者引擎库里面已经嵌入了.so文件(比如百度地图sdk就提供了各种.so文件)

  so是与平台相关的二进制机器码,Android应用支持的cpu架构取决于APK中位于lib或jniLib目录中的.so文件。(Native Libs Monitor这个应用可以帮助我们理解手机上安装的APK用到了哪些.so文件,以及.so文件来源于哪些函数库或者框架。当然,我们也可以自己对app反编译来获取这些信息,不过相对麻烦一些。)

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

  那么,Android 为什么要使用.so文件呢?

  由于Android基于Linux Kernl的,也继承了Linux中所有so相关的设计。除了系统方面的原因,Android开发者还要知道以下几点:

so机制让开发者最大化利用已有的C和C++代码,达到重用的效果,利用软件世界积累了几十年的优秀代码
so是二进制,没有解释编译的开消,用so实现的功能比纯java实现的功能要快
so内存分配不受Dalivik/ART的单个应用限制,减少OOM

二、获取android手机的cpu架构

(此部分转载自:获取Android手机CPU类型 ARM、ARMV7、NEON
手机连接电脑(确保驱动已经正确安装)后,通过cmd打开终端,并以此输入下列命令(仅针对windows系统):
  adb shell ——> cd /proc ——> cat cpuinfo

  结果如下图:
这里写图片描述

  上面中注明的转载文章处有 获取cpu的是arm指令集,armv7指令集、还是neon指令集的代码 及 调用的该函数的示例方法。

三、关于cpu架构简析:

1、预备知识
四大cpu体系结构:ARM、X86/Atom、MIPS、PowerPC

其中ARM/MIPS/PowerPC均是基于精简指令集机器处理器的架构;
X86则是基于复杂指令集的架构,Atom是x86或者是x86指令集的精简版。
根据各种新闻,Android在支持各种处理器的现状:
ARM+Android 最早发展、完善的支持,主要在手机市场、上网本、智能等市场;
X86+Android 有比较完善的发展。有atom+Android的上网本,且支持Atom+Android 和 Atom+Window7双系统;
MIPS+Android 目前在移植、完善过程中;
Powpc+Android 目前在移植、完善过程中。

各架构的详情可参看 此文

2、android的cpu架构

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。

关于ABI:(英语:application binary interface,缩写为 ABI)描述了应用程序(或者其他类型)和操作系统之间或其他应用程序的低级接口。
ABI涵盖了各种细节,如:
1、数据类型的大小、布局和对齐;
2、调用约定(控制着函数的参数如何传送以及如何接受返回值),例如,是所有的参数都通过栈传递,还是部分参数通过寄存器传递;
哪个寄存器用于哪个函数参数;通过栈传递的第一个函数参数是最先push到栈上还是最后;
3、系统调用的编码和一个应用如何向操作系统进行系统调用;
4、以及在一个完整的操作系统ABI中,目标文件的二进制格式、程序库等等。
一个完整的ABI,像Intel二进制兼容标准(iBCS)[1],允许支持它的操作系统上的程序不经修改在其他支持此ABI的操作系统上运行。
ABI不同于应用程序接口(API),API定义了源代码和库之间的接口,因此同样的代码可以在支持这个API的任何系统中编译, 
 然而ABI允许编译好的目标代码在使用兼容ABI的系统中无需改动就能运行。 

扩展:EABI
  嵌入式应用二进制接口指定了文件格式、数据类型、寄存器使用、堆积组织优化和在一个嵌入式软件中的参数的标准约定。开发者使用自己的汇编语言也可以使用EABI作为与兼容的编译器生成的汇编语言的接口。 支持EABI的编译器创建的目标文件可以和使用类似编译器产生的代码兼容,这样允许开发者链接一个由不同编译器产生的库。EABI与关于通用计算机的ABI的主要区别是应用程序代码中允许使用特权指令,不需要动态链接(有时是禁止的),和更紧凑的堆栈帧组织用来节省内存。广泛使用EABI的有Power PC和ARM.

四、开发中对.so文件的处理(主要内容转载自简书的文章

处理.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或者低下的性能。

主要针对so文件的提供者:1、注意编译平台

  使用NDK时,你可能会倾向于使用最新的编译平台,但事实上这是错误的,因为NDK平台不是向后兼容的,而是向前兼容的,可以适当忽略“compile against the latest platform”这类优化提示。简单说,利用NDK针对android-17生成的so文件可以在android-22上运行,反之却不行。这点与Android SDK的兼容性不一样,在SDK14上编译的应用,在API23上也是可以运行的;在SDk23上的编译的应用,只要minSdkVersion小于14,同样在API14上可以运行。所以,推荐使用app的minSdkVersion对应的编译平台。
  这也意味着当你引入一个预编译好的.so文件时,你需要检查它被编译所用的平台版本。

  2、混合使用不同C++运行时编译的.so文件

  .so文件可以依赖于不同的C++运行时,静态编译或者动态加载。混合使用不同版本的C++运行时可能导致很多奇怪的crash,是应该避免的。作为一个经验法则,当只有一个.so文件时,静态编译C++运行时是没问题的,否则当存在多个.so文件时,应该让所有的.so文件都动态链接相同的C++运行时。
这意味着当引入一个新的预编译.so文件,而且项目中还存在其他的.so文件时,我们需要首先确认新引入的.so文件使用的C++运行时是否和已经存在的.so文件一致。

  3、没有为每个支持的CPU架构提供对应的.so文件

  这一点在前文已经说到了,但你应该真的特别注意它,因为它可能发生在根本没有意识到的情况下。
  例如:你的app支持armeabi-v7a和x86架构,然后使用Android Studio新增了一个函数库依赖,这个函数库包含.so文件并支持更多的CPU架构,例如新增android-gif-drawable函数库:

compilepl.droidsonroids.gif:android-gif-drawable:1.1.+’

发布我们的app后,会发现它在某些设备上会发生Crash,例如Galaxy S6,最终可以发现只有64位目录下的.so文件被安装进手机。
解决方案:重新编译我们的.so文件使其支持缺失的ABIs,或者设置

ndk.abiFilters

显示指定支持的ABIs。


最后一点:如果你是一个SDK提供者,但提供的函数库不支持所有的ABIs,那你将会搞砸你的用户,因为他们能支持的ABIs必将只能少于你提供的。

对于采用so的应用开发者:1、将.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安装后,在小于Android5.0的系统中,.so文件位于app的nativeLibraryPath目录中;在大于等于Android 5.0的系统中,.so文件位于app的nativeLibraryRootDir/CPU_ARCH目录中。

  2只提供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
        }
    }
 }
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值