1.什么是CPU架构和ABIAndroid系统目前支持以下七种不同的CPU架构:ARMv***RMv7(2010年起)、x86(2011年起)、MIPS(2012年起)、ARMv8、MIPS64和x86_64(2014年起),各与相应的ABI关联。应用程序二进制接口(ApplicationBinaryInterface)定义了二进制文件(尤其是.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应用支持的ABI依赖于APK中lib/ABI目录下的.so文件,其中的ABI可能是上述7种ABI之一。NativeLibsMonitorApp可以帮助我们了解手机安装的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文件存在。3、.so文件应该放在哪里我们经常对.so文件应该放在哪里或者生成在哪里感到困惑。下面是一个总结:AndroidStudio项目放在main/jniLibs/ABI目录下(当然你也可以通过在build.gradle文件中设置jniLibs.srcDir属性来自己指定)Eclipse项目放在libs/ABI目录(这也是ndk-build命令默认生成.so文件的目录)AAR压缩包位于jni/ABI目录下(引用的APK中会自动包含.so文件AAR压缩包)通过最终APK文件中lib/ABI目录下的PackageManager安装后,.so文件在Android5.0以下系统中位于app的nativeLibraryPath目录下;在Android5.0等系统中,.so文件位于应用程序的nativeLibraryRootDir/CPU_ARCH目录中。4.安装Apk时,PackageManagerService选择解压so文件的策略在Android系统中,我们安装Apk文件时,会将lib目录下的so文件解压到App原库目录下,一般放在/data下/data在/package-name/lib目录下,复制策略根据系统和CPU架构不同,so文件配置不正确。比如一些app使用第三方so时,只配置其中一个。CPU架构可能会导致某些机型出现应用程序适配问题。Android版本so拷贝策略问题五、配置so的建议针对Android系统的这些拷贝策略问题,我们给出一些配置so的建议:5.1ABIforarmeabi和armeabi-v7a方法一:由于armeabi-v7a指令集为兼容armeabi指令集,所以如果可以接受损失一些应用性能,又不想保留两份库,可以去掉armeabi-v7a目录及其下的库文件,只保留armeabi目录;比如Apk使用第三方时只有armeabi的ABI,可以考虑去掉Apk中lib目录下的armeabi-v7a目录。方法二:将so拷贝一份到armeabi和armeabi-v7a目录下。5.2对于x86目前市面上的x86机型,为了兼容arm命令,基本上都内置了libhoudini模块,即二进制转码支持。该模块负责将ARM命令转换为x86命令,所以如果是由于Apk包的大小,并且可以接受一些性能损失,可以选择删除x86库目录,armeabi中的so库x86下配置的目录也可以正常加载使用。5.3对于64位的ABI,如果app开发者打算支持64位,那么必须包含所有64位的so,否则可以选择不单独编译64位的so,使用所有的32位的so,64位机型支持32位所以默认加载。例如,Apk使用第三方,因此只有32位ABI。可以考虑去掉Apk中lib目录下的64位ABI子目录,保证Apk安装后可以正常使用。5.AndroidStudio配置abiFiltersandroid{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错误的种类很多,分类如下:/Couldnotfindthecorrespondingfunctionjava.lang.UnsatisfiedLinkError:Cannotloadlibrary:load_library//无法加载库的原因:很明显,上面崩溃的根本原因是:(1)so无法加载,可能是so不存在(2)so正常加载,但是没有找到对应的函数。对于第二个原因,显然是比较容易排错的,而且在开发中,这样的函数调用必须在编译时和debug模式下进行测试,所以这个原因出现的概率很小。那么下面主要总结一下上述“so无法加载”导致crash的几种原因:6.1生成so本身缺陷的一个简单例子:crashstack:java.lang.UnsatisfiedLinkError:Cannotloadlibrary:find_library(linker.cpp:889):"/data/data/com.netease.nis.apptestunit/app_lib/libdemo.so"之前在java.lang.Runtime.load(Runtime.java:340)atjava.lang.System.load(System.java:521)atcom.netease.nis.bugrpt.ReLinker.loadLibrary(ReLinker.java:76)atcom.example.crash.MainActivity.onCreate(MainActivity.java:272)atandroid.app.Activity.performCreate(Activity.java:5220)atandroid.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1086)atandroid.app.ActivityThread.performLaunchActivity(ActivityThread.java:2193)atandroid.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2279)atandroid.app。ActivityThread0.access6Activity(java:142)atandroid.app.ActivityThread$H.handleMessage(ActivityThread.java:1272)atandroid.os.Handler.dispatchMessage(Handler.java:99)atandroid.os.Looper.loop(Looper.java:137)atandroid.app.ActivityThread.main(ActivityThread.java:5105)atjava.lang.reflect.Method.invokeNative(NativeMethod)atjava.lang.reflect.Method.invoke(Method.java:511)atcom.android.internal。os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)atcom.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)atdalvik.system.NativeStart.main(NativeMethod)解决方法:查看原项目Application。mk,发现APP_STL:=gnustl_shared原方案使用共享库,不一定支持所有机型,所以使用静态库gnustl_static解决。相应的,在AndroidStudio中,共享库需要替换为静态库gnustl_static。这种关于so编译共享库的问题需要检查一下。App_STL可用值system系统默认stlport_static-使用STLport作为静态库stlport_shared-使用STLport作为共享库gnustl_static-使用GNUlibstdc++作为静态库gnustl_shared-使用GNUlibstdc++作为共享库失败考虑共享库的模型匹配导致UnsatisfiedLinkError崩溃,其次是64位和32位系统架构问题,也可能导致UnsatisfiedLinkError崩溃。6.2移动设备没有空间。当so生成正确后,会根据设置的支持so库框架生成相应的库。在Android系统中,当我们安装Apk文件时,lib目录下的so文件会被解压到App的原生库目录下,一般在/data/data/package-name/lib目录下,当准备好加载so时native层的,虽然Apk中有对应的so文件,但是由于移动设备没有足够的空间加载so,导致加载失败,出现上面的crash。6.3so配置错误如果so生成正确且手机空间充足,如前所述,在Android系统中,我们在安装Apk文件时,会将lib目录下的so文件解压到原来的库目录下应用程序。据说放在/data/data/package-name/lib目录下。但是,根据系统和CPU架构的不同,复制策略也不同。如果so文件配置错误,比如某些app使用第三方so时,只配置了其中一个CPU架构so,可能会导致app在部分机型上出现适配问题,导致上述crash。6.4AndroidPackageManager安装问题用户安装了与手机CPU架构不匹配的Apk安装包,或者App升级过程中由于各种原因导致so文件没有正确发布。这种问题可以使用ReLinker来解决。使用ReLinker非常简单,只需使用ReLinker.loadLibrary(context,"mylibrary")而不是标准的。System.loadLibrary("我的图书馆");
