Android开发------关于.so文件的那些事

作者:hwj3747
转载请注明

前言

在Android应用开发的时候,经常会需要集成其他SDK,而这些SDK很多都需要导入一些.so文件。这个时候,就很容易出现一个问题:

java.lang.UnsatisfiedLinkError: 
dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.test.test-1/base.apk"],
nativeLibraryDirectories=[/data/app/com.test.test-1/lib/arm64, /vendor/lib64, /system/lib64]]] couldn't find "xxxxxxxxxxxxxxx.so"

乍一看,这个问题不就是找不到一个.so文件吗。其实不然,大多数情况下,我们都是有导入这些文件的,只是系统找不到而已,那么系统为什么会找不到?要怎么样才能让系统找到这些文件?要明白这些问题,首先我们得先了解一下.so文件。

.so文件与Android CPU架构

首先,了解一下什么是.so文件

.so文件是Linux下的程序函数库,即编译好的可以供其他程序使用的代码和数据
一般来说.so文件就是常说的动态链接库, 都是C或C++编译出来的。
Linux下的.so文件时不能直接运行的,一般来讲,.so文件称为共享库

不过,对于应用开发者而言,我们并不关心.so文件时怎么生成的。我们只需要清楚一点,.so文件是用C或C++写的,所以更偏底层一点,它的执行时与CPU架构息息相关的。所以我们想要让带有.so文件的程序在Android手机上跑起来,首先我们要明白,Android手机的CPU架构与.so文件的联系。

Android系统目前支持以下七种不同的CPU架构:ARMv5,ARMv7 (从2010年起),x86 (从2011年起),MIPS (从2012年起),ARMv8,MIPS64和x86_64 (从2014年起),每一种都关联着一个相应的ABI(应用程序二进制接口)。

每一个CPU架构对应一个ABI:armeabi,armeabi-v7a,x86,mips,arm64-v8a,mips64,x86_64。

当然,并不是说一种设备就支持一种ABI,只是最优先支持的是这种ABI而已。例如,在x86设备上,libs/x86目录中如果存在.so文件的话,会被安装,如果不存在,则会选择armeabi-v7a中的.so文件,如果也不存在,则选择armeabi目录中的.so文件(因为x86设备也支持armeabi-v7a和armeabi)。

那么,怎么知道我们手机支持的是哪种ABI呢,只需要这么一句代码就可以获取ABI了:

String CPU_ABI = android.os.Build.CPU_ABI;

以下是各个设备的兼容情况,可根据其兼容情况,选择相应的SO目录。

armeabi设备只兼容armeabi;
armeabi-v7a设备兼容armeabi-v7a、armeabi;
arm64-v8a设备兼容arm64-v8a、armeabi-v7a、armeabi;
X86设备兼容X86、armeabi;
X86_64设备兼容X86_64、X86、armeabi;
mips64设备兼容mips64、mips;
mips只兼容mips;

在Android设备上使用.so文件

在了解了Android的CPU架构以及ABI的关系后,我们就可以来尝试解决前面遇到的找不到so文件的问题了。

  • 首先,有可能是.so文件放错目录了,当然一般不会出现这种问题,只要按如下目录放置就行。
    Android Studio工程放在jniLibs/ABI目录中(当然也可以通过在build.gradle文件中的设置jniLibs.srcDir属性自己指定)

  • 还有一种可能就是第三方给我们的so包不支持我们的设备支持的ABI,比如第三方的so包是放在arm64-v8a目录下的,而我们手机只支持armeabi,这个时候有两种解决方法。
    第一种就是联系第三方提供支持我们设备的so包,虽然这种方法是最好的,但是基本上很难做到。
    第二种就是自己建一个armeabi目录,把arm64-v8a目录下的包放到armeabi目录下,大多数情况下是可行的。

  • 最后就是最头疼的一种问题了,就是我们的程序使用了多个so包,这个时候可能不同的第三方提供商提供的是支持不同ABI的包,如果我们按照一般的方法来导入肯定是会出问题。
    举个例子,我有一个应用,里面引用了2个module,第一个module的jniLibs/ABI目录下有一个armeabi目录存放so包,第二个module的jniLibs/ABI目录有一个armeabi-v7a目录存放so包,理论上来说armeabi-v7a是兼容armeabi的,所以应该不会有问题,但是实际情况是生成APK后会生成armeabi-v7a和armeabi两个目录,而我们的设备是优先选择armeabi-v7a目录寻找so包的。然鹅!当设备选择完armeabi-v7a目录之后,它就不会再去其他ABI目录上找so包了。也就是说我们的第一个module里面的so包都会找不到了!这个时候就会出现一开始的问题了。
    并且有的时候有些地方是隐式使用so包的,这个时候你看不到,所以你需要先把程序打成一个APK包,然后再打开压缩包,如下:

2.JPG

可以看到apk目录的lib下就有我们所使用的各种ABI。

既然已经知道了原因:设备只会按其支持的ABI,按照优先级找so包,并且找到之后就不会再去别的ABI目录找了。这样,我们就可以想到解决问题的方法了,比如上图,我们打开之后又3个ABI目录,arm64-v8a的设备肯定就会从这个目录开始查找,所以我们可以把所有的.so文件都放到同一个目录’armeabi’目录下,再指定程序只在这个目录下查找so文件。
代码如下:

android {
defaultConfig {
......
        ndk {
                   abiFilters 'armeabi'
        }
    }
}

这样一来,就完美的解决这个问题了!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值