首页 > 编程知识 正文

小知识,.so是什么文件

时间:2023-05-04 16:47:53 阅读:46963 作者:986

1. 什么是CPU架构及ABI

Android系统目前支持ARMv5、ARMv7、2010年及更高版本、x86、2011年及更高版本、MIPS、2012年及更高版本、ARMv8、MIPS64、x86_64和2014年及更高版本的七种不同的CPU体系结构

“APP应用程序二进制接口”(Application Binary Interface )表示从要使用的指令集到可用系统函数库的二进制(特别是. so文件)对应系统预编程程序在Android系统上,每一个CPU架构对应一个ABI:armeabi,armeabi-v7a,x86,mips,arm64-v8a,mips64,x86_64

2. 为什么需要重点关注.so文件

很明显,如果项目使用NDK,则会生成. so文件,因此您已经注意到了。 您可能认为如果只是使用Java语言进行编码,就没有必要关注. so文件。 因为Java是一个跨平台。 但是,实际上,即使项目中只使用Java语言,您也可能没有意识到项目依赖的函数库和引擎库中嵌入了. so文件,它们依赖于不同的ABI。 例如,如果在项目中使用RenderScript支持库、OpenCV、Unity、android-gif-drawable、SQLCipher等,则生成的APK文件将包含. so文件需要关注. so文件。

Android APP应用程序支持的ABI依赖于apk lib/ABI目录中的. so文件。 其中,ABI可能是上述七种ABI之一。

名为Native Libs Monitor的APP应用程序有助于了解手机上安装的APK使用的是哪个. so文件,以及. so文件来自哪个函数库或框架。 当然,我们也可以自己反编译APP获取这些信息,但是比较麻烦。

很多设备都支持多于一种的ABI,例如ARM64和x86设备也可以同时运行armeabi-v7a和armeabi的二进制包但是,建议为特定平台提供相应平台的二进制软件包。 在这种情况下,由于最近的体系结构更新,如硬件fpu、更多寄存器和更好的矢量化,将减少一个模拟层以提高性能。 在Build.SUPPORTED_ABIS中,可以根据优先级获取设备支持的ABI列表。 但是,不应该从APP应用程序中读取它。 Android软件包管理器安装APK时,如果相应的lib/ABI目录中存在. so文件,则会自动从APK软件包中为相应的系统ABI选择预编译的. so文件

3. .so文件应该放在什么地方

很多情况下,我不知道应该将. so文件放在哪里,或者应该在哪里生成。 以下是总结。

Android Studio项目位于主/JNI libs/ABI目录中。 当然,也可以通过设置build.gradle文件的jniLibs.srcDir属性来自行指定。 Eclipse项目位于libs/ABI目录中。 (这也是ndk-build命令。在最终APK文件的lib/ABI目录中使用PackageManager安装ABI目录(APK将自动包含. so文件,以引用AAR压缩包)后在Android 5.0或更高版本的系统上, so文件位于app的nativeLibraryRootDir/CPU_ARCH目录中。4. 安装Apk时PackageManagerService选择解压so文件的策略

在Android系统上,安装Apk文件时,lib目录下的so文件将解压缩到App的本机库目录中。 通常位于/data/data/package-name/lib目录下,但复制策略因系统和CPU体系结构而异,无法正确配置so文件

安卓版so复制策略问题4.0遍历apk中的文件,如果apk中lib目录下的主abi子目录中存在so文件,则复制主abi子目录下的所有so; 只有在主abi子目录下没有so文件时,才会复制子目录下的so文件。 如果so不正确,安装Apk将导致副本不完整。 例如,如果Apk的lib目录中存在三个so文件: armeabi/libx.so、armeabi/liby.so和armeabi-v7a/libx.so,则主abi为armeabi-v7a 遍历4.0-4.0.3apk中的所有文件,如果符合so文件的规则,并且是主abi目录或子abi目录下的so,则将其解压缩并复制到相应目录。 存在同名的so盖,

比如一个App的armeabi和armeabi-v7a目录下都包含同名的so,那么就会发生覆盖现象,覆盖的先后顺序根据so文件对应ZipFileR0中的hash值而定,考虑这样一个例子,假设一个Apk同时有armeabi/libx.so和armeabi-v7a/libx.so,安装到主abi为armeabi-v7a的手机上,拷贝so时根据遍历顺序,存在一种可能即armeab-v7a/libx.so优先遍历并被拷贝,随后armeabi/libx.so被遍历拷贝,覆盖了前者。本来应该加载armeabi-v7a目录下的so,结果按照这个策略拷贝了armeabi目录下的so。> 4.0.4遍历Apk中文件,当遍历到有主abi目录的so时,拷贝并设置标记hasPrimaryAbi为真,以后遍历则只拷贝主abi目录下的so,当标记为假的时候,如果遍历的so的entry名包含次abi字符串,则拷贝该so。经过实际测试,so放置不当时,安装Apk时存在so拷贝不全的情况。这个策略想解决的问题是在4.0~4.0.3系统中的so随意覆盖的问题,即如果有主abi目录的so则拷贝,如果主abi目录不存在这个so则拷贝次abi目录的so,但代码逻辑是根据ZipFileR0的遍历顺序来决定是否拷贝so,假设存在这样的Apk,lib目录下存在armeabi/libx.so, armeabi/liby.so, armeabi-v7a/libx.so这三个so文件,且hash的顺序为armeabi-v7a/libx.so在armeabi/liby.so之前,则Apk安装的时候liby.so根本不会被拷贝,因为按照拷贝策略,armeabi-v7a/libx.so会优先遍历到,由于它是主abi目录的so文件,所以标记被设置了,当遍历到armeabi/liby.so时,由于标记被设置为真,liby.so的拷贝就被忽略了,从而在加载liby.so的时候会报异常。64位分别处理32位和64位abi目录的so拷贝,abi由遍历Apk结果的所有so中符合bilist列表的最靠前的序号决定,然后拷贝该abi目录下的so文件。策略假定每个abi目录下的so都放置完全的,这是和4.0以前版本一样的处理逻辑,存在遗漏拷贝so的可能。

5. 配置so的建议

针对Android 系统的这些拷贝策略的问题,我们给出了一些配置so的建议:

5.1 针对armeabi和armeabi-v7a两种ABI

方法1:由于armeabi-v7a指令集兼容armeabi指令集,所以如果损失一些应用的性能是可以接受的,同时不希望保留库的两份拷贝,可以移除armeabi-v7a目录和其下的库文件,只保留armeabi目录;比如Apk使用第三方的so只有armeabi这一种ABI时,可以考虑去掉Apk中lib目录下armeabi-v7a目录。

方法2:在armeabi和armeabi-v7a目录下各放入一份so。

5.2 针对x86

目前市面上的x86机型,为了兼容arm指令,基本都内置libhoudini模块,即二进制转码支持,该模块负责把ARM指令转换为x86指令,所以如果是出于Apk包大小的考虑,并且可以接受一些性能损失,可以选择删掉x86库目录,x86下配置的armeabi目录的so库一样可以正常加载使用。

5.3 针对64位ABI

如果App开发者打算支持64位,那么64位的so要放全,否则可以选择不单独编译64位的so,全部使用32位的so,64位机型默认支持32位so的加载。比如Apk使用第三方的so只有32位ABI的so,可以考虑去掉Apk中lib目录下的64位ABI子目录,保证Apk安装后正常使用。

5. Android Studio配置abiFilters

android { defaultConfig { ndk { abiFilters 'armeabi-v7a' //, 'armeabi', 'arm64-v8a', 'x86', 'x86_64', 'mips', 'mips64' } }}

这句话的意思就是指定NDK需要兼容的架构,把除了armeabi-v7a以外的兼容包都过滤掉,只剩下一个armeabi-v7a的文件夹。

即使我们没有指定其他的兼容框架,也需要一个过滤。当我们接入多个第三方库时,很可能第三方库做了多个平台的兼容。譬如fresco就做了各个平台的兼容,所以它创建了各个兼容平台的目录。因为只要出现了这个目录,系统就只会在这个目录里找.so文件而不会遍历其他的目录,所以就出现了找不到.so文件的情况。

6. java.lang.UnsatisfiedLinkError

该错误类型较多,以下进行分类:

java.lang.UnsatisfiedLinkError : dlopen failed: library //dlopen打开失败java.lang.UnsatisfiedLinkError :findLibrary returned null //找不到libraryjava.lang.UnsatisfiedLinkError : Native method not found //找不到对应函数java.lang.UnsatisfiedLinkError :Cannot load library: load_library //无法load library

出现原因:

显然出现上述崩溃的根本原因是:

(1)so无法加载,可能是so不存在等原因

(2)so正常加载,但是没有找到相应的函数

针对第二个原因,显然相对来说很容易排查,而且在开发中,这样的函数调用必然会在编译时和debug模式下进行测试,所以这种原因产生的概率很小。

那么下面主要总结几类“so无法加载”而导致上述崩溃的几种原因:

6.1 生成的so本身缺陷

一个简单的例子:

crash堆栈:

java.lang.UnsatisfiedLinkError: Cannot load library: find_library(linker.cpp:889): "/data/data/com.netease.nis.apptestunit/app_lib/libdemo.so" failed to load previously at java.lang.Runtime.load(Runtime.java:340) at java.lang.System.load(System.java:521) at com.netease.nis.bugrpt.ReLinker.loadLibrary(ReLinker.java:76) at com.example.crash.MainActivity.onCreate(MainActivity.java:272) at android.app.Activity.performCreate(Activity.java:5220) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1086) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2193) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2279) at android.app.ActivityThread.access$600(ActivityThread.java:142) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1272) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:137) at android.app.ActivityThread.main(ActivityThread.java:5105) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:511) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560) at dalvik.system.NativeStart.main(Native Method)

解决方法:

查看原项目Application.mk,发现APP_STL := gnustl_shared。原方案使用的是共享库,这不一定都支持所有的机型,改用静态库gnustl_static问题解决。
对应的在Android Studio中需要将共享库改用静态库gnustl_static。这一类关于so编译共享库问题,需要进行检查。

APP_STL 可用值system 系统默认stlport_static - 使用STLport作为静态库stlport_shared - 使用STLport 作为共享库gnustl_static - 使用GNU libstdc++ 作为静态库gnustl_shared - 使用GNU libstdc++ 作为共享库

上述例子只是一个简单的例子,可能在so编译生成时,由于没有考虑共享库的机型匹配等原因导致UnsatisfiedLinkError崩溃,其次是64位32位系统架构问题,也可能导致UnsatisfiedLinkError崩溃。

6.2 手机设备没有空间

在so正确生成情况下,会根据设置的支持so库框架生成对应的库。在Android系统中,当我们安装Apk文件的时候,lib目录下的so文件会被解压到App的原生库目录,一般来说是放到/data/data/package-name/lib目录下,当准备加载native层的so时,虽然在Apk中有对应的so文件,但是由于手机设备没有足够的空间加载该so,导致加载失败,产生上述崩溃。

6.3 so配置错误

倘若so正确生成,且手机空间充足,那么如上所述,在Android系统中,当我们安装Apk文件的时候,lib目录下的so文件会被解压到App的原生库目录,一般来说是放到/data/data/package-name/lib目录下。但是根据系统和CPU架构的不同,其拷贝策略也是不一样的。倘若不正确地配置了so文件,比如某些App使用第三方的so时,只配置了其中某一种CPU架构的so,可能会造成App在某些机型上的适配问题,产生上述崩溃。

6.4 Android的PackageManager安装问题

用户安装了与手机CPU架构不符的Apk安装包,或者App升级过程中因各种原因未正确释放so文件。这种问题可以使用ReLinker解决。

使用ReLinker十分简单,使用

ReLinker.loadLibrary(context, “mylibrary”)

代替标准的即可。

System.loadLibrary(“mylibrary”);

参考:

[1] http://www.kejik.com/article/21884.html

[2] http://blog.csdn.net/wove55678/article/details/52313208

[3] http://crash.163.com/index.do#news/!newsId=5

[4] http://blog.csdn.net/crash163/article/details/51895987

[5] http://crash.163.com/index.do#news/!newsId=4

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。