动态加载so

为什么使用so

  • so机制让开发者最大化利用已有的C和C++代码,达到重用的效果;
  • so是二进制,没有解释编译的开消,用so实现的功能比纯java实现的功能要快;
  • so内存分配不受Dalivik/ART的单个应用限制,减少OOM;
  • 相对于java代码,二进制代码的反编译难度更大,一些核心代码可以考虑放在so中。

查看android系统支持的ABI

Android可以在运行期间确定当前系统所支持的ABI,这是由系统编译时的具体参数指定的:

  • primary ABI(主ABI):对应当前系统中使用的机器码类型
  • secondary ABI(副ABI):表示当前系统支持的其他ABI类型

因此很多手机不止支持一个ABI。

通过adb命令查看

/system/build.prop中指定了支持的ABI类型,在adb中,可使用如下命令查看:

adb shell getprop | grep abilist

通过API获取

使用Build.SUPPORTED_ABIS可以获取当前设备支持的ABI列表

String supportedAbis = Build.SUPPORTED_ABIS;

apk如何找到so

扫描整个apk文件,先找主abi的so文件,即找路径格式为:

lib/<primary-abi>/lib<name>.so

如果没有找到,则找合适的次级abi的so文件

lib/<secondary-abi>/lib<name>.so

即安装应用时,系统会根据当前CPU架构选择最优ABI适配,如果找到了合适的so文件,包管理器会将该ABI文件夹下所有so库全部拷贝至应用的data目录下:

data/data/<package_name>/lib/

注意:apk安装过程对so选择是基于整个ABI文件夹的,而非以单个so文件为粒度,也就是说把lib/armeabi 、lib/armeabi-v7a、lib/x86等等文件夹的其中一个文件夹内所有.so复制到应用的data目录下。

找不到so时,会报java.lang.UnsatisfiedLinkError的错误。

so加载方式

System.loadLibrary

静态加载so库,只需要传入so在Android.mk中定义的LOCAL_MODULE的值即可。

系统会调用System.mapLibraryName把这个libName转化成对应平台的so的全称并去尝试寻找这个so加载。

System.load

动态加载so库,需要给出完整的绝对路径。so必须拷贝到so拷贝至/data/data/<package-name>/下。因为外部存储路径是一种不可执行的存储媒介。

load只能加载2种路径下的so:

  • /system/lib
  • 应用程序安装包的路径

loadLibrary和load最终都会调用nativeLoad(name, loader, ldLibraryPath)方法,只是因为loadLibrary的参数传入的仅仅是so的文件名,所以,loadLibrary需要首先找到这个文件的路径,然后加载这个so文件。 而load传入的参数是一个文件路径,所以它不需要去寻找这个文件路径,而是直接通过这个路径来加载so文件。

一般的优化方案

  • 按照ABI分别单独打包APK(如Google Play)
  • 只提供armabi的so

进阶版方案

首先是性能问题:使用兼容模式去运行arm架构的so,会丢失专门为当前ABI优化过的性能;其次还有兼容性问题,虽然x86设备能兼容arm类型的函数库,但是并不意味着100%的兼容,某些情况下还是会发生crash,所以x86的arm兼容只是一个折中方案,为了最好的利用x86自身的性能和避免兼容性问题,我们最好的做法仍是专为x86提供对应的so。 针对这些问题,我们可以采用一个相对更好的方案:让所有so都来自于网路,应用下载服务器上的so库后,利用System.load方法动态加载当前设备对应的so。

注意项

如果我们的应用选择了支持多个ABI,要十分注意:对于每个ABI下的so,但要么全部支持,要么都不支持。不应该混合着使用,而应该为每个ABI目录提供对应的.so文件。

因为是按照ABI来将so拷贝到私有目录的,如果部分有,则会在找这个ABI下的没有的so库,则直接报错。

一种可行的处理方案是:取你所有的so库所支持的ABI的交集,移除其他。

results matching ""

    No results matching ""