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方法。我们进去看看

@Overridepublic 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库

更多相关文章

  1. Android APK 文件自动安装
  2. Android读写文件二
  3. Android处理9.png文件流程
  4. Attribute is missing the Android namespace prefix——android
  5. Android文件系统的结构及目录用途、操作方法 整理
  6. Android类加载器源码分析
  7. Android实现ListView异步加载图片

随机推荐

  1. Android水面落叶动态壁纸源码及分析 附下
  2. android 的Android(安卓)Media Scanner多
  3. 增加android模拟器的内存大小
  4. Android(安卓)音视频编解码(二) -- Media
  5. Android(安卓)socket 编程 实现消息推送(
  6. android之动画实现(一)(四种基本补间动画)
  7. 【Android(安卓)FFMPEG 开发】Android(安
  8. Android(安卓)多线程
  9. Android第一行代码读书笔记
  10. Android应用程序消息处理机制(Looper、Han