Android JNI学习笔记——so文件动态加载

Android中对于so的加载提供了两个方法。

System.loadLibrary("libName");
System.load("pathName");
/**
 * See {@link Runtime#load}.
 */
public static void load(String pathName) {
    Runtime.getRuntime().load(pathName, VMStack.getCallingClassLoader());
}

/**
 * See {@link Runtime#loadLibrary}.
 */
public static void loadLibrary(String libName) {
    Runtime.getRuntime().loadLibrary(libName, VMStack.getCallingClassLoader());
}

其中loadLibrary我们只需要传入so文件的名称就可以了,它会根据传进来的libName,扫描APK内部的nativeLibrary目录,获取并返回内部SO库文件的完整路径filename,然后加载这个so文件。

load方法就为我们外部加载so文件提供了可能,它需要指定一个文件路径,也就是说我们可以使用这个方法来指定我们要加载的so文件的路径来动态的加载so文件。

其实,loadLibrary和load最终都会调用nativeLoad(name, loader, ldLibraryPath)方法,只是因为loadLibrary的参数传入的仅仅是so的文件名,所以,loadLibrary需要首先找到这个文件的路径,然后加载这个so文件。
而load传入的参数是一个文件路径,所以它不需要去寻找这个文件路径,而是直接通过这个路径来加载so文件。

具体我们来看看代码。

先看看System.loadLibrary,这里调用了Runtime的loadLibrary

/*
 * Searches for and loads the given shared library using the given ClassLoader.
 */
void loadLibrary(String libraryName, ClassLoader loader) {
    if (loader != null) {
        String filename = loader.findLibrary(libraryName);
        String error = doLoad(filename, loader);
        return;
    }
    ……
}

它执行的是BaseDexClassLoader的findLibrary方法。我们进去看看

@Override
public String findLibrary(String name) {
    return pathList.findLibrary(name);
}

再看进去DexPathList类

public String findLibrary(String libraryName) {
    String fileName = System.mapLibraryName(libraryName);
    for (File directory : nativeLibraryDirectories) {
        File file = new File(directory, fileName);
        if (file.exists() && file.isFile() && file.canRead()) {
            return file.getPath();
        }
    }
    return null;
}

根据传进来的libName,扫描APK内部的nativeLibrary目录,获取并返回内部SO库文件的完整路径filename。再回到Runtime类,获取filename后调用了“doLoad”方法。

private String doLoad(String name, ClassLoader loader) {
    String ldLibraryPath = null;
    String dexPath = null;
    if (loader == null) {
        ldLibraryPath = System.getProperty("java.library.path");
    } else if (loader instanceof BaseDexClassLoader) {
        BaseDexClassLoader dexClassLoader = (BaseDexClassLoader) loader;
        ldLibraryPath = dexClassLoader.getLdLibraryPath();
    }
    synchronized (this) {
        return nativeLoad(name, loader, ldLibraryPath);
    }
}

调用Native方法“nativeLoad”,通过完整的SO库路径filename,把目标SO库加载进来。

我们再看看System.load方法,调用Runtime的load方法

void load(String absolutePath, ClassLoader loader) {
    if (absolutePath == null) {
        throw new NullPointerException("absolutePath == null");
    }
    String error = doLoad(absolutePath, loader);
    if (error != null) {
        throw new UnsatisfiedLinkError(error);
    }
}

看到没有,它直接调用的就是doLoad方法。也就是说它直接根据传入的完整路径来加载so文件。

所以,如果我们希望动态的加载so文件,我们就可以使用System.load方法来加载。

另外需要注意的是,System.load方法指定的so文件的路径不能为SD卡的路径,因为SD卡等外部存储路径是一种可拆卸的(mounted)不可执行(noexec)的储存媒介,不能直接用来作为可执行文件的运行目录,使用前应该把可执行文件复制到APP内部存储再运行。

所以,假如我们从网络下发一个so文件,下载下来之后,我们首先需要把他复制到应用内部目录下,然后使用System.load方法加载,这样我们就可以调用这个本地方法了。

参考文章:Android动态加载补充 加载SD卡中的SO库

欢迎关注微信公众号:DroidMind
精品内容独家发布平台


呈现与博客不一样的技术干货

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值