Android 加载 SO 库 UnsatisfiedLinkError 错误的原因及解决方案

Android 应用开发者应该对 UnsatisfiedLinkError 这种类型的错误比较熟悉了,这个问题一直困扰着广大的开发者,那么有没有想过有可能你什么都没做错,也会出现这个问题呢?

我们在 Android 应用开发测试过程中曾经碰到过这样的案例,apk 在某机型上安装完成之后运行即崩溃,报错 UnsatisfiedLinkError。


java.lang.UnsatisfiedLinkError: Couldn’t load mobsec from loader dalvik.system.PathClassLoader.....findLibrary returned null


首先怀疑是在 apk 中相应的 libs\abi 目录下没有放置 libmobsec.so,然而检查发现这个 so 在所有的 libs\abi 下都有放置过,继续排查;

然后的想法是放置的 so 不是对应 abi 的,比如由于粗心在 armeabi 目录下放置了 x86 指令集的 so,导致在 armeabi 指令集手机上加载出错,这个也被排除掉;

就在没有头绪的时候,想到 System.loadLibrary 函数加载 so 时,系统是从指定的路径下加载的,那么这个路径下 so 是否存在呢?

我们知道应用的私有 Native library 目录 /data/data/packagename/lib 是一个符号链接,链接到 /data/app-lib/ 目录,System.loadLibrary 是到这个目录去尝试加载 so 的。

adb shell 到这个路径下,使用命令 ls 查看,果然这个 libmobsec.so 是不存在的。那么是什么原因导致的呢?

分析 Android 系统源码的实现,发现 /data/app-lib/ 这个目录下的 so ,是在系统安装 apk 时从 apk 的 lib 目录下去抽取的。

Android 加载 SO 库 UnsatisfiedLinkError 错误的原因及解决方案_第1张图片

在安装 app 时,Android package manager 代码需要分析当前手机支持的指令集并拷贝相关指令集的 so。从 Android2.X 到 Android6.0 系统,由于相继加入了 x86、64位等指令集的支持,这一部分代码处理逻辑有不少变动,然而这个代码是存在逻辑缺陷的,存在遗漏拷贝的可能,导致在一些机型上并不一定保证所有的 so 都能被正确抽取到 /data/app-lib/ 目录下,从而导致应用在加载 so 的时候出现 UnsatisfiedLinkError 这样的错误。

已经有开发者意识到这个 bug,比如在 Chromium 的源代码的一段注释,说明了 Android package manager 中的问题:



                    * PackageManager may fail to update shared library.
                    *
                    * Native library directory in an updated package is a symbolic link
                    * to a directory in /data/app-lib/, for example:
                    * /data/data/com.android.chrome/lib -> /data/app-lib/com.android.chrome[-1].
                    * When updating the application, the PackageManager create a new directory,
                    * e.g., /data/app-lib/com.android.chrome-2, and remove the old symlink and
                    * recreate one to the new directory. However, on some devices (e.g. Sony Xperia),
                    * the symlink was updated, but fails to extract new native libraries from
                    * the new apk.
                    

“在 Android 平台上加载本地库的危险性”这篇文章中提到了作者遇到同样的问题,并基于 Chromium 给出的一种权宜的解决办法:封装 System.loadLibrary 接口为 ReLinker 接口,如果发现无法正常加载 so,则获取 apk 路径并解压相应指令集的 so,然后尝试去加载。这种方案经过验证是可以显著减少 UnsatisfiedLinkError 错误的出现,下图为作者使用了 ReLinker 接口后的日上报 UnsatisfiedLinkError 错误数的变化趋势图。

Android 加载 SO 库 UnsatisfiedLinkError 错误的原因及解决方案_第2张图片

ReLinker 接口现在已经集成到网易云捕的SDK中,使用方法如下:

ReLinker.loadLibrary(context, “mylibrary”);

来代替

System.loadLibrary(“mylibrary”);

你可能感兴趣的:(Android 加载 SO 库 UnsatisfiedLinkError 错误的原因及解决方案)