[](

)动态加载网络或文件夹下的so库

加载某文件夹 -> 相应架构的so文件

Apk文件本身就是一个压缩文件,解压后目录结构大致如下:

动态加载 so 注意事项

[](

)某apk文件的解压目录

[](

)动态加载so案例

我先把完整的代码贴出来,然后讲解可能遇到的两个错误。

动态加载的核心类(根据abi从本地选择合适的so库加载)


public class DynamicSO {

    private static final String TAG = DynamicSO.class.getSimpleName();

    public static void loadExSo(Context context,String soName, String soFilesDir){

        File soFile = choose(soFilesDir,soName);

        String destFileName = context.getDir("myso", Context.MODE_PRIVATE).getAbsolutePath()  + File.separator + soName;

        File destFile = new File(destFileName);

        if (soFile != null) {

            Log.e(TAG, "最终选择加载的so路径: " + soFile.getAbsolutePath());

            Log.e(TAG, "写入so的路径: " + destFileName);

            boolean flag = FileUtil.copyFile(soFile, destFile);

            if (flag) {

                System.load(destFileName);

            }

        }

    }

    /**

     * 在网络或者本地下载过的so文件夹: 选择适合当前设备的so文件

     *

     * @param soFilesDir so文件的目录, 如apk文件解压后的 Amusic/libs/ 目录 : 包含[arm64-v8a,arm64-v7a等]

     * @param soName so库的文件名, 如 libmusic.so

     * @return 最终匹配合适的so文件

     */

    private static File choose(String soFilesDir,String soName) {

        if (Build.VERSION.SDK_INT >= 21) {

            String [] abis = Build.SUPPORTED_ABIS;

            for (String abi : abis) {

                Log.e(TAG, "SUPPORTED_ABIS =============> " + abi);

            }

            for (String abi : abis) {

                File file = new File(soFilesDir,abi + File.separator + soName);

                if (file.exists()) {

                    return file;

                }

            }

        } else {

            File file = new File(soFilesDir, Build.CPU_ABI + File.separator + soName);

            if (file.exists()) {

                return file;

            } else {

                // 没有找到和Build.CPU_ABI 匹配的值,那么就委屈设备使用armeabi算了.

                File finnalFile = new File(soFilesDir, "armeabi" + File.separator + soName);

                if (finnalFile.exists()) {

                    return finnalFile;

                }

            }

        }

        return null;

    }

}

动态调用so的函数,不需要System.loadLibrary.


public class Security {

    public native String stringFromJNI();

}

测试类,我的需要加载的so文件都是放在sdcard/mylibs目录下的。


public class TestActivity extends AppCompatActivity {

    public void handle(View view) {

        DynamicSO.loadExSo(this,"libnative-lib.so", Environment.getExternalStorageDirectory() + "/mylibs");

        // JNI 调用

        Security security = new Security();

        String message = security.stringFromJNI();

        Toast.makeText(this, message, Toast.LENGTH_LONG).show();

    }

}

[](

)常见错误


1. Exception: dlopen failed: "/data/data/com.less.tplayer.baidu/app_myso/libnative-lib.so" has bad ELF magic

2. E/art: dlopen("/data/data/com.less.tplayer.baidu/app_myso/libnative-lib.so", RTLD_LAZY) failed: dlopen failed: "/data/data/com.less.tplayer.baidu/app_myso/libnative-lib.so" is 32-bit instead of 64-bit

3. E/art: dlopen("/data/data/com.less.tplayer.baidu/app_myso/libnative-lib.so", RTLD_LAZY) failed: dlopen failed: "/data/data/com.less.tplayer.baidu/app_myso/libnative-lib.so" is 64-bit instead of 32-bit

【错误1】非常简单,但却耗费我一晚上都没找到错误,Google搜到的也是不相干的,错误提示太坑了,什么是精灵魔法??我还以为是5.0版本问题,然后测试4.0,然后以为so的写入目录有问题,然后。。。


byte[] bytes = new byte[1024];

int len = -1;

while ( (len = bufferedInputStream.read(bytes)) != -1) {

    bufferedOutputStream.write(bytes, 0, len);

}

拐了一大圈,最后是


bufferedInputStream.read(bytes)

bufferedInputStream.read()

TNN的,啥时候bytes丢了!!!

这个不起眼的小错误差点搞得我放弃这个知识点。

上面的粗心大意的错误终于解决了,却又出现了下面的错误,真坑!

【错误2】


E/art: dlopen("/data/data/com.less.tplayer.baidu/app_myso/libnative-lib.so", RTLD_LAZY) failed: dlopen failed: "/data/data/com.less.tplayer.baidu/app_myso/libnative-lib.so" is 32-bit instead of 64-bit

这个问题在Google某位大神尼古拉斯*赵四 的帮助下找到了答案:

我特意用 [vivoX9照亮你的美 arm64-v8a架构] 测试了下:


String [] abis = Build.SUPPORTED_ABIS;

    for (String abi : abis) {

      Log.e(TAG, "SUPPORTED_ABIS =============> " + abi);

  }

}

打印结果是:


E/DynamicSO: SUPPORTED_ABIS =============> arm64-v8a

E/DynamicSO: SUPPORTED_ABIS =============> armeabi-v7a

E/DynamicSO: SUPPORTED_ABIS =============> armeabi

这些顺序是按照优先级排列的,最适合的在最上面,兼容的在下面。

前面注意事项中也提到过:说各个module之间so的架构一定要对应,如果我们的App里面包含了64位的架构arm64-v8a文件夹,那么这时候应用的ApplicationInfo的abi就是arm64-v8a了,就会发送消息给Zygote64的进程,创建的也是64位的虚拟机了,如果我们的App应用里面只包含的是armeabi-v7a和armeabi的文件夹,那么创建的会是32位的虚拟机以兼容模式运行。

最后

那我们该怎么做才能做到年薪60万+呢,对于程序员来说,只有不断学习,不断提升自己的实力。我之前有篇文章提到过,感兴趣的可以看看,到底要学习哪些知识才能达到年薪60万+。

通过职友集数据可以查看,以北京 Android 相关岗位为例,其中 【20k-30k】 薪酬的 Android 工程师,占到了整体从业者的 30.8%!

北京 Android 工程师「工资收入水平 」

动态加载 so 注意事项

今天重点内容是怎么去学,怎么提高自己的技术。

1.合理安排时间

2.找对好的系统的学习资料

3.有老师带,可以随时解决问题

4.有明确的学习路线

CodeChina开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》

当然图中有什么需要补充的或者是需要改善的,可以在评论区写下来,一起交流学习。

动态加载 so 注意事项