这里我们来解决Android应用动态替换.so的问题。

.so 文件默认存放路径与加载方法

Android NDK会编译JNI相关代码生成.so文件。生成的.so通常会位于 $PROJECT_DIR/libs/armeabi/ 目录,在应用打包之后,在Java代码中可以直接加载位于默认目录的.so文件。


// .so:libdynamicso.so

static {
    System.loadLibrary("dynamicso");
}

System.loadLibrary 方法会去默认几个目录寻找名为libdynamicso.so的文件进行加载。在无需动态修改.so的情况下,一般会在类的静态初始化代码块中做这件事情,待到类加载完成之后就可以在Java代码里调用JNI函数了。

加载非默认目录的.so文件

在解决动态更新之前,先要解决从文件系统其他目录加载.so的问题。应用加载.so需要在文件系统相应目录有执行权限。对于Android SD卡上的文件,应用没有执行权限,因此需要将.so文件拷贝到具备执行权限的目录。Android应用对于其对应的数据目录具备执行权限,因此可以将.so文件拷贝到数据目录之后再执行加载。

// 获取数据目录路径
PackageManager pm = getPackageManager();
String dataDir = pm.getApplicationInfo(getPackageName(), 0).dataDir;
// 从文件路径加载.so
File so = new File(dataDir, “libdynamicso.so”);
System.load(so.getPath());

从服务器下载.so文件进行加载

解决了加载文件系统上的.so的问题之后,动态更新.so就变得简单了。进行替换的步骤大体分为下面几步,

  1. 应用传递.so版本到服务器判断有无更新
  2. 若.so需更新,则下载.so文件到应用数据目录
  3. 从应用数据目录加载.so文件

需要注意的地方

应用启动加载.so之后,同名的.so不能再次被加载。没有找到卸载.so的方法,因此在进行动态更新的时候需要在加载本地.so之前进行,否则不能加载成功。

调用Activity的finish方法退出应用之后不能保证当前进程清理干净,因此最好退出的时候将自身进程结束。

完整示例

实现了一个示例代码,支持加载本地.so与从网络更新.so,参见 dynamicso