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方法。我们进去看看
@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库
更多相关文章
- Android APK 文件自动安装
- Android读写文件二
- Android处理9.png文件流程
- Attribute is missing the Android namespace prefix——android
- Android文件系统的结构及目录用途、操作方法 整理
- Android类加载器源码分析
- Android实现ListView异步加载图片