Android ABI想到的

作者: Chefil | 来源:发表于2017-03-25 11:20 被阅读206次

    缘起

    最近在项目中用到了realm,引起项目编译找不到so库,原来是realm的多版本so库所导致的。所以,解铃还须系铃人,咱们就从ABI入手解决这个问题。

    ABI是什么

    应用程序二进制接口ABI(Application Binary Interface)定义了二进制文件(尤其是.so文件)如何运行在相应的系统平台上,从使用的指令集,内存对齐到可用的系统函数库。在Android 系统上,每一个CPU架构对应一个ABI:armeabi,armeabi-v7a,x86,mips,arm64- v8a,mips64,x86_64。分别对应着下面7种CPU架构ARMv5,ARMv7 (从2010年起),x86 (从2011年起),MIPS (从2012年起),ARMv8,MIPS64和x86_64 (从2014年起),每一种都关联着一个相应的ABI。

    .so文件

    .so文件是unix的动态连接库,是二进制文件,在Android中调用动态库文件(*.so)都是通过jni的方式,这里我们要说说NDK,Android NDK是一个工具集,它让我们能够在Android应用程序中使用由本地代码(native code)编写的代码模块,NDK 提供的编译系统让我们可以高效的编译源码,不需要去处理工具链、平台等细节。我们只需要创建很短的编译文件,描述哪些源文件要编译以及哪些应用程序会使用 它们,编译系统就会编译源代码并且将编译生成的共享库(shared libraries)直接放到我们的应用程序中。如果项目中使用到了NDK,它将会生成.so文件,因此显然你已经在关注它了。如果只是使用Java语言进行编码,你可能在想不需要关注.so文 件了吧,因为Java是跨平台的。但事实上,即使你在项目中只是使用Java语言,很多情况下,你可能并没有意识到项目中依赖的函数库或者引擎库里面已经 嵌入了.so文件,并依赖于不同的ABI,Android应用支持的ABI取决于APK中位于lib/ABI目录中的.so文件。Native Libs Monitor这个应用可以帮助我们理解手机上安装的APK用到了哪些.so文件,以及.so文件来源于哪些函数库或者框架。

    当然,我们也可以自己对app反编译来获取这些信息,不过相对麻烦一些,其实我们只要把APK文件解压了以后就可以看到项目用到了哪些.so文件了。如下图

    1.png

    ABI和CPU的关系

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

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

    2.png

    配置.so文件

    使用 app源根文件夹下build.gradle文件的配置:

    android {
        compileSdkVersion 25
        buildToolsVersion "25.0.2"
        defaultConfig {
            applicationId "com.dygsm.dywifi"
            minSdkVersion 15
            targetSdkVersion 22
            versionCode 1
            versionName "04.30.00.01"
            testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
            // 不声明ndk标签,项目默认会创建一个libapp.so的文件
            ndk {
                abiFilters "armeabi", "armeabi-v7a", "x86"
            }
        }
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }
    }
    

    此外,要在 gradle.properties 文件中添加android.useDeprecatedNdk=true,重新编译工程即可。
      不同CPU架构的Android手机加载时会在libs下找自己对应的目录,从对应的目录下寻找需要的.so文件;如果没有对应的目录,就会去armeabi下去寻找,如果已经有对应的目录,但是如果没有找到对应的.so文件,也不会去armeabi下去寻找了。 所以,这里需要注意工程配置哪几个so文件目录,需要加载对应的so文件,不然会报错。

    如何适配各个目录,例如有一些第三方的类库只提供了armeabi下的.so文件,而工程配置不止armeabi一个目录,这就需要将armeabi下的.so文件复制到其他对应的目录下。果第三方提供了不同平台的.so文件,则复制不同平台的.so文件到项目中对应的文件夹下即可。

    so文件也会影响编译出的apk大小(将apk解压出来,lib目录下就为so文件目录),所以只配置armeabi一个目录,既能适配各CPU架构的手机,也能精简apk大小(微信就是只有armeabi一个目录)。

    使用原则

    你应该尽可能的提供专为每个ABI优化过的.so文件,你不应该混合着使用(不能就装对不同cpu架构的so文件,放在同一个ABI目录下)。你应该为每个ABI目录提供对应的.so文件。
    当只有一个.so文件时,静态编译C++运行时是没问题的,
    当存在多个.so文件时,应该让所有的.so文件都动态链接相同的C++运行时。
    这意味着当引入一个新的预编译.so文件,而且项目中还存在其他的.so文件时,我们需要首先确认新引入的.so文件使用的C++运行时是否和已经存在的.so文件一致。

    缘灭

    缘起的时候出现的编译错误解决方法如下:

    1. 不使用realm。
    2. 在每个ABI文件下面复制相同的.so库。
    3. 适配的使用库运行和支持运行。

    相关文章

      网友评论

        本文标题:Android ABI想到的

        本文链接:https://www.haomeiwen.com/subject/astcottx.html