Android so库动态加载总结

相比静态加载so,动态加载so最大好处是减少包的体积,节省一笔流量费用(针对使用移动卡的设备)。可以网络下发so文件到指定目录,再拷贝到可动态加载的文件目录下/data/data/packageName
或者网络下载so文件到可动态加载的文件目录下/data/data/packageName。国际规则:
https://github.com/kellysong/android-blog-demo/tree/master/soloader-demo

本demo支持从sd卡目录或者assets目录拷贝so文件到app私有目录

so加载问题

需要注意的是:直接拷贝so到app的私有目录,通过System.loadLibrary(soName)或System.load(soPath)加载时还是会报错的

Caused by: java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader

我们需要通过ClassLoader修改系统默认加载的so路径applicationInfo.nativeLibraryDir,替换成之前拷贝so到app的私有目录并放在DexPathList数组的第一位。

原理如下:

修改前so目录:

/data/app/com.sjl.soloader-nNYWatxQW10z30V7YgV9Og==/lib/arm64

DexPathList[[zip file "/data/app/com.sjl.soloader-nNYWatxQW10z30V7YgV9Og==/base.apk"],nativeLibraryDirectories=[/data/app/com.sjl.soloader-nNYWatxQW10z30V7YgV9Og==/lib/arm64, /system/lib64, /system/vendor/lib64]]

修改后so目录:

/data/user/0/com.sjl.soloader/app_libs/arm64-v8a //这是我们直接拷贝so到app的私有目录

DexPathList[[zip file "/data/app/com.sjl.soloader-nNYWatxQW10z30V7YgV9Og==/base.apk"],nativeLibraryDirectories=[/data/user/0/com.sjl.soloader/app_libs/arm64-v8a, /data/app/com.sjl.soloader-nNYWatxQW10z30V7YgV9Og==/lib/arm64, /system/lib64, /system/vendor/lib64, /system/lib64, /system/vendor/lib64, /system/lib64, /system/vendor/lib64, /system/lib64, /system/vendor/lib64]]

拷贝和加载的部分代码:

 private void loadSoFromAssetPathCopy() {
        List out = new ArrayList<>();
        try {
            SoUtils.copyAssetsDirectory(this, "so", out);
        } catch (Exception e) {
            e.printStackTrace();
        }
        //arm64-v8a, armeabi-v7a, armeabi
        Log.d(TAG, "supported api:" + Build.CPU_ABI + " " + Build.CPU_ABI2);

        if (Build.VERSION.SDK_INT >= 21) {
            String[] abis = Build.SUPPORTED_ABIS;
            for (String abi : abis) {
                if (loadSoFile(abi, out)) {
                    break;
                }
            }
        } else {
        if (TextUtils.isEmpty(Build.CPU_ABI)) {
            if (loadSoFile(Build.CPU_ABI, out)) {
                return;
            }
        }
        if (TextUtils.isEmpty(Build.CPU_ABI2)) {
            if (loadSoFile(Build.CPU_ABI2, out)) {
                return;
            }
        }
        if (loadSoFile("armeabi", out)) {
            return;
        }
    }

}

private boolean loadSoFile(String abi, List out) {
    boolean success = false;
    for (final String soPath : out) {
        if (soPath.contains(abi)) {
            String parentDir = SoUtils.getParentDir(new File(soPath));
            //注入so路径,如果清除了的话。没有清除可以不用每次注入
            boolean b = SoLoader.loadSoFile(this, parentDir);
            //加载so库
            if (b && new File(soPath).exists()) {
                final String soName = soPath.substring(soPath.lastIndexOf("/") + 1, soPath.lastIndexOf(".")).substring(3);
            /*    final String soName = soPath.substring(soPath.lastIndexOf("/") + 1, soPath.lastIndexOf(".")).substring(3);
                System.loadLibrary(soName); //加载有可能直接崩掉
                //System.load(soPath); //load使用的是文件绝对路径
                */
                //or 使用第三方库加载,这个加载报错回调failure,不会直接崩溃,底层也是 System.load实现,只不过加载之前做了一些验证
                ReLinker.loadLibrary(this, soName, new ReLinker.LoadListener() {
                    @Override
                    public void success() {
                        Log.i(TAG, "加载成功:" + soPath);
                    }

                    @Override
                    public void failure(Throwable t) {
                        Log.e(TAG, "加载异常:" + soPath, t);
                    }
                });
                success = true;
                break;
            }
        }
    }
    return success;
}

补充一点:

在Android系统上,每一个CPU架构对应一个ABI:armeabi,armeabi-v7a,x86,mips,arm64-v8a,mips64,x86_64
一般系统都支持多种abi,假设系统CPU架构是arm64-v8a,只适配arm64-v8a可以大幅降低包体积。当我们指定加载arm64-v8a,我们可以在gradle下,指定过滤cpu架构,避免出现没必要的错误

ndk {
        abiFilters "arm64-v8a"
    }

这时候项目只剩下arm64-v8a(包括第三方库中的so),项目中那些可以动态下发的so库,可以不配置在项目中,直接从网络下载加载。

注意需要先加载后使用native方法。

参考

https://blog.csdn.net/hujin2017/article/details/102804883

你可能感兴趣的:(Android,jni,so库动态加载)