美文网首页app开发Android札记Android
关于Android的.so文件你所需要知道的

关于Android的.so文件你所需要知道的

作者: asce1885 | 来源:发表于2015-08-18 11:39 被阅读115470次

    @author ASCE1885的 Github 简书 微博 CSDN
    原文链接

    广而告之时间:我的新书《Android 高级进阶》(https://item.jd.com/10821975932.html在京东开始预售了,欢迎订购!

    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文件的话。

    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中如下配置:

    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高级进阶》一书的读者可免费加入,更详细的内容见这篇文章:http://www.jianshu.com/p/eaacc5b68960

    711fce22gy1fewpp2qmewj20go0nswfz.jpg

    相关文章

      网友评论

      • 83f09ed364b9:谬见,不妨将"backward compatible"、"forward compatible"翻译为"向过去兼容"、"向未来兼容",消歧义。
      • 龙少侠linux:x86的so文件体积很大、而适配的机型较少、一般不需要打包到apk中
      • 梦痕_简书:链接点了 说文章违规被锁定了呢
      • 03a9db709e59:splits {
        abi {
        enable true
        reset()
        include "x86", "x86_64", "armeabi-v7a", "arm64-v8a" //select ABIs to build APKs for
        universalApk true//是否打包一个通用版本的apk,默认false
        }
        }
        报错:Error:Execution failed for task ':app:process_360Arm64-v8aDebugResources'.
        > Cannot invoke method replaceAll() on null object :worried:
      • 林夕2008:意思就是我只添加armeabi的.so库,就能支持大部分手机了吗?
        lovedabaozi:@AndSync armeabi v7是向下兼容 armeabi的, 反之则不行,所有只放armeabi就可以,只是有些性能损耗。
        SnapKit:楼主也没有一个定论,从支持的角度来讲这样是可以的,但又说性能可能会有影响 因此这种方式是不对的 哈哈 目前绝大多数手机是 armeabi-v7a', 'arm64-v8a 这两种的 如果只放一种的话 那倒不如用armeabi-v7a的
      • 08_carmelo:如果我就想混合使用so文件(一个moduler只放32位so,另一个放32和64),有办法做到吗?
      • Avalon1:,《Android高级进阶》一书的读者可免费加入,更详细的内容见这篇文章:http://www.jianshu.com/p/eaacc5b68960这个网址挂了哎
        asce1885:@Avalon1 http://mp.weixin.qq.com/s/CeEF6GZVUACkOmZW6tUvbw
      • longzekai:有用。好东西。
      • 阿V很简单:请教楼主,使用别人的so库文件,有什么办法可以知道so库编译的安卓平台版本?
      • 相互交流:楼主知道,调用.SO文件,返回的是字符串,但是返回的乱码,,有遇到过吗,知道怎么解决吗?还望楼主解答...
        asce1885:@相互交流 编码问题吧
      • sendtion:看来只放armeabi就可以了,so文件的确挺占体积的
      • 厂里的帅小伙:拉下来看评论,结果大多数在说妹子,可是我却没有看到啊
      • captainary:测试发现:
        "但你不应该从你的应用程序中读取它,因为Android包管理器安装APK时,会自动选择APK包中为对应系统ABI预编译好的.so文件,如果在对应的lib/ABI目录中存在.so文件的话。"

        这句话是不正确的,我手上的好几部测试机,并不会自动的查找,需要我们手动的去设置.so的目录,不知道这个是不是环境,或者什么配置造成的.
        所以,不管什么原因,如果APP只保留一个armeabi目录,必须显式的指定它的ABI目录

        module的gradle中添加:
        defaultConfig {
        ndk {
        abiFilters 'armeabi'
        }
        }

        同时在项目的根目录下,找到gradle.properties(不存在则新建)
        android.useDeprecatedNdk=true
        表明我们自己定义目录,不让系统自动找.so文件
        captainary:@刻舟求剑KJ 你说的这个确实有道理,哈哈,我还没发现呢,按照你这么说法,确实是有可能的:blush:
        刻舟求剑KJ:是不是因为你们的app里的某些依赖会生成全部 abi 目录的 so 文件,所以其实你编译以后会有所有的 abi 目录,你可以在打包编译后解包看一下,我也是猜测的,不知道是否正确,望交流
      • 小马哥nice:"以减少APK包大小为由是一个错误的借口,因为你也可以选择在应用市场上传指定ABI版本的APK,生成不同ABI版本的APK可以在build.gradle中如下配置"
        大神您好,请问这个的原理是?看不懂这一点,能否讲下?非常感谢大神了,你的书,我准备去淘宝买,非常值得!
        asce1885:@小马哥nice arm的so可以在其他架构上兼容运行,但不能保证百分百兼容,且有性能问题
        小马哥nice:@asce1885 嗯嗯 谢谢! 我有一点很奇怪 就是微信和淘宝的so文件只有一个平台 那其他手机怎么兼容到呢?
        asce1885:@小马哥nice 这一点只针对Google Play而言,就是它会根据用户手机的abi下发对应abi的apk,而不是下发全量abi的apk
      • CalvinNing:这篇文章必须mark一下,太赞了,从文章到下面的留言评论提问解答真是“极好的” :smiley: ,反正看完之后虽然不能说全懂了,但是不懂得地方也知道怎么去搜,搜什么关键词。博主的书我放购物车了,等1212一起买。
      • 花老鼠:博主,请教个问题,在生成so加解密库的时候,armeabi下的so库的加解密是可以的,但是当我用arm84-v8a手机测试的时候,加密就是失败的,你知道什么原因吗?生成不同架构so和什么有关系??
      • 北有花开:你好,我最近就遇到了UnsatisfiedLinkError这个错误!你知道是怎么回事吗?我只提供了armeabi架构的.so文件!单独的demo可以运行!但是导入我自己项目就报错!希望能指点一下!卡这儿一周了
        asce1885:@PoisonH 文章里面有提到,你百度下ndk.abiFilters
        北有花开:@asce1885 没有用到其他架构的so,ndkfilter怎么设置?我用的是https://github.com/CrazyOrr/FFmpegRecorder 这个,单独运行它的可以,但是将所有的东西,复制到我的项目下,就会出现UnsatisfiedLinkError这个错误!我的是小米2S。我设置了 jniLibs.srcDirs = ['libs']。
        asce1885:@PoisonH 查看项目中是否有用到其他架构的so,引入的sdk呢,有设置ndkfilter没?
      • 短尾狸:按照博文,arm64找不到,系统自己回去找armeabi armeabi-v7a 这里的so文件。奇怪的是我这里一个红米手机,armeabi armeabi-v7a里有32位的so文件,arm64里没有。然后报了这样的错 dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.test.appname-1/base.apk"],nativeLibraryDirectories=[/data/app/com.test.appname-1/lib/arm64, /data/app/com.test.appname-1/base.apk!/lib/arm64-v8a, /vendor/lib64, /system/lib64]]] couldn't find "libEZTLIB.so"
        短尾狸:@九卦十不准 是啊,实际的效果是这样的。但是我以为上面的博文写的是会找不到64位的后自动寻找32位? :smiley:
        九卦十不准:你提供arm64文件夹,系统安装就只会拷贝这个文件夹的so了,所以要么在arm64下提供全部的so,要么不提供arm6文件夹
        短尾狸:@meow23333333 貌似并没有再去找armeabi 跟armeabi-v7a里的so文件了 真是不科学啊。。。
      • 工程师milter:有个小建议供参考:
        原文第三段开头一句,即“应用程序二进制接口(Application Binary Interface)定义了二进制文件(尤其是.so文件)如何运行在相应的系统平台上”感觉翻译的不是很准确。原文是“An Application Binary Interface is the definition of how binaries (esp. .so files) should be made in order to work on the platform”。我觉得翻译成这样更好:“应用程序二进制接口定义了其所对应的CPU架构能够执行的二进制文件(特别是.so文件)的格式规范”
        asce1885:@milter :+1:
      • 606fd5f5448c:不错,赞一个
      • d0604aee2730:受教了! 受益颇深..

        有一点: "推荐使用app的minSdkVersion对应的编译平台", 编译平台怎么选择, 如何制定某个编译平台编译啊?
        oliversmallgan:@MarkBeng 刚好最近在做ndk开发,具体来说有三种方式,第一,可以在Application.mk文件中指定,加上APP_PLATFORM=android-xx,“xx”代表了android level,一般选择8;第二,使用eclipse编译时,project.properties文件起到了指定android level的作用;第三,可以在ndk-build命令后面带参数APP_PLATFORM=android-xx;
      • eoeoops:博主写篇如何约模特的教程吧 :smile:
        asce1885:@大柴胡君 不懂:grin:
      • 4b823c73a214:不错的文章,脑中的疑问全解决了
        asce1885:@洁癖君 :smile:
      • 086daf2df374:为什么 我只留下armeabi 有的会报错呢
        asce1885:@111111445555 肯定不能保证啊,质量参差不齐的
        086daf2df374:@asce1885 第三方的一些 是不是不能保证%100 兼容啊
        asce1885:@111111445555 不是百分百兼容的
      • e3e03b041a20:你好,能不能说说怎么把armeabi下的so文件编译成armeabi-v7a、x86和arm64-v8a的so文件。。。NDK新手,请教一下
        asce1885:@丶菁华浮梦 so只能从源码编译
        e3e03b041a20:@asce1885 ……你说的是自己编写的c代码,我想解决的是怎么把一个现有的armeabi的so文件编译成其他类型的so文件,我不知道android.mk和application.mk文件……
        asce1885:@丶菁华浮梦 在Application.mk中设置:

        APP_ABI := armeabi armeabi-v7a mips x86

        不过前提是你的C++代码兼容你所需要的处理器架构
      • c1ad1bf4c508:在x86设备上,libs/x86目录中如果存在.so文件的话,会被安装,如果不存在,则会选择armeabi-v7a中的.so文件,如果也不存在,则选择armeabi目录中的.so文件。。。 其他自己的so都在armeabi目录下的时候, 在v7a目录下只用第三方库的so时。 crash了, UnsatisfiedLinkError, 说是找不到我自己的so, 并没有加载armeai里的, 这是什么情况?
        92773dfb137e:@_一念 你的lib中存在2个目录。armeabi中是你自己的so,armeabi-v7a中是第三方的so。运行时arm安卓系统会优先看是否存在armeabi-v7a,如果不存在才会去看armeabi。两个文件夹同时存在的话,系统只会安装armeabi-v7a中的so,所以你找不到armeabi里你自己的
        c1ad1bf4c508:@asce1885 查看了安装后,lib目录下的的so,我自己的so有几个没有在其中,大部分都有在该目录下,请问一下,是什么原因。
        asce1885:@_一念 文章里面说了:你应该尽可能的提供专为每个ABI优化过的.so文件,但要么全部支持,要么都不支持:你不应该混合着使用。
      • e3485e61b381:这么漂亮的妹子,应该介绍给我
        asce1885:@e3485e61b381 :grin:
      • 31940b14b024:原来是译文,难怪读起来有点不顺 :smile:
      • asce1885:感谢@snakeliu的支持 :smile:
      • 江上夜泊人5728:这么漂亮的妹子,应该介绍给我
        asce1885:@江上夜泊人5728 ⊙̆̈_⊙̆̈ 什么鬼
      • Geckhan的读书笔记本:刚入门的程序员,上面没看太懂,为了最后一张妹子也点个赞!
        asce1885:@努力爬行的壁虎 :smile:
        Geckhan的读书笔记本:@asce1885 写的不错照的更好啊
        asce1885:@努力爬行的壁虎 😁,妹子也是我的摄影作品

      本文标题:关于Android的.so文件你所需要知道的

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