Android用Java代码不知如何加载so包

问题:不知道如何加载so包
过程:在Android Studio用Java代码运行一个Demo(原始音频录播)时发现运行失败,代码出错在so包没有加载,目前明确知道代码没错,但是不知道so包如何加载(有jni文件)
现在烦请各位教教我如何解决该问题

img

img

在 Android 中,so 库的加载是由 Dalvik 虚拟机通过 JNI 接口来完成的。因此,在 Java 代码中无法直接加载 so 库。要加载一个 so 库,需要考虑以下几个方面:

确认你的 so 库是否已经被正确编译和生成。通常情况下,Android Studio 会自动为你编译生成 so 库。

将生成的 so 库文件放到正确的位置。在 Android 中,so 库默认存放在 app/src/main/jniLibs 目录下,其中 main 目录是必须存在的,而 jniLibs 则是需要手动创建的,目录名字必须是这样的,否则无法正常加载。

在 Java 代码中通过 System.loadLibrary 或者 System.load 方法来加载 so 库。其中,System.loadLibrary 用于加载预编译库(即将 .so 文件打包到 APK 中),而 System.load 则用于加载外部库(即从外部文件系统中加载 .so 文件)。

例如,如果你想加载名为 "native-lib" 的预编译库,可以在 Java 代码中使用以下语句来加载:

java

static {
    System.loadLibrary("native-lib");
}

这里假设你的 so 文件名为 libnative-lib.so,那么它应该位于 app/src/main/jniLibs/armeabi-v7a/ 目录下,其中 armeabi-v7a 是针对 ARM 处理器的 ABI 类型,不同的 ABI 类型对应不同的目录名。

如果你想加载外部库,可以使用以下语句来加载:

java

static {
    System.load("/path/to/libnative-lib.so");
}

其中 /path/to/ 是你的 .so 文件所在的路径。

请注意,Java 代码中加载 so 库需要在 AndroidManifest.xml 文件中添加以下权限:

xml

这是因为 Dalvik 虚拟机会通过网络获取和加载库文件。

  • 文章:android 加壳与破解--静态修改so,常用破解方法 中也许有你想要的答案,请看下吧
  • 除此之外, 这篇博客: Android 性能优化系列:包体积优化中的 远程 so 动态加载方案 部分也许能够解决你的问题, 你可以仔细阅读以下内容或跳转源博客中阅读:
  • 在这里插入图片描述

    在这里插入图片描述

    so 动态加载其实就是把本应该打包进 apk 的 so 库通过网络的方式,经过 md5、加解密等安全性校验后下载下来,再通过插件化的方式将下载的 so 正常装载使用的方案。

    从代码层面考虑就是我们要将下载的 so 文件路径插入到 DexPathList 的 nativeLibraryDirectories 列表,这样就能让 ClassLoader 查找到我们的 so 文件路径正确加载出来。这里参考 Tinker 的思路写个简单的 demo 了解思路:

    private void install(ClassLoader classLoader, File soAbsoluteFilePathDir) throws Throwable {
        // 获取 DexClassLoader 的 DexPathList
        final Field pathListField = classLoader.getClass().getDeclaredField("pathList");
        pathListField.setAccessible(true);
        final Object dexPathList = pathListField.get(classLoader);
    
        // 获取 DexPathList 的 nativeLibraryDirectories
        final Field nativeLibraryDirectories = dexPathList.getClass().getDeclaredField("nativeLibraryDirectories");
        nativeLibraryDirectories.setAccessible(true);
    
        List<File> origLibDirs = (List<File>) nativeLibraryDirectories.get(dexPathList);
        if (origLibDirs == null) {
            origLibDirs = new ArrayList<>(2);
        }
        // 如果 so 已经存在,移除路径重新装载
        final Iterator<File> libDirIt = origLibDirs.iterator();
        while (libDirIt.hasNext()) {
            final File libDir = libDirIt.next();
            if (soAbsoluteFilePathDir.equals(libDir)) {
                libDirIt.remove();
                break;
            }
        }
        // 在索引为 0 的位置加入远程 so 文件路径
        origLibDirs.add(0, soAbsoluteFilePathDir);
    
        // 获取 DexPathList 的 systemNativeLibraryDirectories
        final Field systemNativeLibraryDirectories = dexPathList.getClass().getDeclaredField("systemNativeLibraryDirectories");
        systemNativeLibraryDirectories.setAccessible(true);
        List<File> origSystemLibDirs = (List<File>) systemNativeLibraryDirectories.get(dexPathList);
        if (origSystemLibDirs == null) {
            origSystemLibDirs = new ArrayList<>(2);
        }
    
        // 将添加了远程 so 的 nativeLibraryDirectories 和 systemNativeLibraryDirectories 的 so 路径装载
        final List<File> newLibDirs = new ArrayList<>(origLibDirs.size() + origSystemLibDirs.size() + 1);
        newLibDirs.addAll(origLibDirs);
        newLibDirs.addAll(origSystemLibDirs);
    
        final Method makeElements = dexPathList.getClass().getDeclaredMethod("makePathElements", List.class);
        makeElements.setAccessible(true);
    
        final Object[] elements = (Object[]) makeElements.invoke(dexPathList, newLibDirs);
    
        final Field nativeLibraryPathElements = dexPathList.getClass().getDeclaredField("nativeLibraryPathElements");
        nativeLibraryPathElements.setAccessible(true);
        nativeLibraryPathElements.set(dexPathList, elements);
    }
    

    当然动态 so 加载还需要考虑很多问题,譬如 so 存在依赖关系、版本控制等等,具体可以参考网上写得比较详细的两篇有关动态 so 加载的文章学习:

    Android动态加载so!这一篇就够了!

    Bugly:动态下发 so 库在 Android APK 安装包瘦身方面的应用

  • 以下回答由chatgpt基于相关博客总结生成:

    我很抱歉,以上参考资料和您的问题并没有直接关联。针对您的问题,我提供以下的解决方案:

    1. 确认so包的正确性:首先要确认您使用的so包是否正确。检查so库是否编译成功,以及它是否与您编译的目标架构兼容。可以使用以下命令进行检查:

      file

      如果返回的结果包含“ELF”,则表示该so库编译成功。

    2. 将so包添加到工程中:您需要将编译好的so库添加到工程中以便程序运行时能够加载。方法如下:

      a. 在工程的“app”目录下创建名为“jniLibs”的文件夹。

      b. 将so库放在该文件夹的对应目录下。例如,如果你的so库针对的是armeabi-v7a架构,则应该将so库放在“jniLibs/armeabi-v7a/”目录下。

    3. 加载so包:您需要编写jni代码来加载so库。具体步骤如下:

      a. 在Java代码中声明native方法,例如:

      public native void loadNativeLibrary();

      b. 编写jni代码实现该方法。示例代码如下:

      include

      JNIEXPORT void JNICALL Java_com_example_MyClass_loadNativeLibrary(JNIEnv env, jobject obj) { // 加载so库 const char libraryName = "libYourLibraryName.so"; jint result = -1; result = env->GetJavaVM(&Jvm); if(result != JNI_OK){ return; } void* handle = dlopen(libraryName, RTLD_LAZY | RTLD_GLOBAL); if (NULL == handle) { return; } }

    4. 调用native方法:在Java代码中调用该native方法即可完成so库的加载。调用代码示例:

    loadNativeLibrary();

    希望我的解决方案对您有所帮助。如果您在实施过程中遇到其他问题,请随时与我联系。

名字写错了或者路劲错了或者配置写错了。
cmake文件贴出来,app的gradle配置贴出来,so库放的路劲贴出来。
三者对比就可以确认。