关于.so文件和NDK工具链编译.sh文件问题

1、关于so文件你所应该知道的
早期的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中的一种。
很多设备都支持多于一种的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。

    defaultConfig {
        ndk {
            abiFilters "armeabi", "armeabi-v7a", "x86"
        }
    }

最后一点: 如果你是一个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等等)。

2、ndk工具链编译.sh文件问题
最近用到直播推流这门技术,网上找了找发现挺多的但是免费的基本没有,找了个开源的yasea还不错吧,就是支持的设备比较少,作者提供了jni文件,部分设备需要自己编译,但是作者没有提供 常规的mk文件,只有sh文件,就不太懂了,钻研了几天有点眉目了,特此记录下来。

参考:编译Android平台libRTMP库
Android NDK Standalone Toolchain

以生成armeabi-v为例子:

#!/bin/sh

ANDROID_NDK=/home/leoma/MyOSP/android-ndk-r13b    //配置ndk目录
SYSROOT=$ANDROID_NDK/platforms/android-21/arch-arm   //指定platforms版本,我们知道ndk是向后兼容的,现在4.4的系统还有很大一部分所以,一般以minSdk来处理,但是你会发现21版本之下的只有三种platforms,即你只能编译32位的abi,这个原因可以看上面,所以如果需要64位的我们指定21版本的platforms即可。
CROSS_PREFIX=$ANDROID_NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi- //工具链路径
EXTRA_CFLAGS="-march=armv7-a -mfloat-abi=softfp -mfpu=neon -D__ANDROID__ -D__ARM_ARCH_7__ -D__ARM_ARCH_7A__" //这个是个麻烦,看不懂,不过给你们列个表,大神总结的
EXTRA_LDFLAGS="-nostdlib"
PREFIX=`pwd`/libs/armeabi-v7a  //生成的文件的目录

./configure --prefix=$PREFIX \
        --host=arm-linux \
        --sysroot=$SYSROOT \
        --cross-prefix=$CROSS_PREFIX \
        --extra-cflags="$EXTRA_CFLAGS" \
        --extra-ldflags="$EXTRA_LDFLAGS" \
        --enable-pic \
        --enable-static \
        --enable-strip \
        --disable-cli \
        --disable-win32thread \
        --disable-avs \
        --disable-swscale \
        --disable-lavf \
        --disable-ffms \
        --disable-gpac \
        --disable-lsmash

make clean
make STRIP= -j8 install || exit 1

cp -f $PREFIX/lib/libx264.a $PREFIX
rm -rf $PREFIX/include $PREFIX/lib $PREFIX/pkgconfig

关于EXTRA_CFLAGS配置问题

armeabi 选项 -march=armv5te -msoft-float
armeabi-v7a 选项 -march=armv7-a -mfpu=vfpv3-d16 -mfloat-abi=softfp -mthumb
arm64-v8a 选项 -march=armv8-a
x86 选项 -mtune=intel -m32 -mmmx -msse2 -msse3 -mssse3
x86_64 选项 -mtune=intel -m64 -mmmx -msse2 -msse3 -mssse3 -msse4.1 -msse4.2 -mpopcnt
mips 选项 -march=mips32 -mfp32 -msoft-float -mno-micromips
mips64 选项 -march=mips64r6

注: 由于MIPS未找到同时使用硬件float并指定CPU:FPU时钟比为2:1的设置方法,因此使用软件float代替

由于以上的.sh文件是给出来的,你真让我自己写,真心不会写,我们只需要配置好自己的需求,然后在linux系统上面 直接 sh xxx.sh即可~ 关于ndk-build 啥的就不说了,老生常谈了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值