Android 开发也好,java 开发也好,都会有遇到过加载 so 库的情况
加载 so 库有两个方法:System.loadLibrary() 和 System.load(),它们有什么区别呢?
package java.lang;
public final class System {
// ....
@CallerSensitive
public static void load(String filename) {
Runtime.getRuntime().load0(Reflection.getCallerClass(), filename);
}
@CallerSensitive
public static void loadLibrary(String libname) {
Runtime.getRuntime().loadLibrary0(Reflection.getCallerClass(), libname);
}
// ....
}
可以看到都是由 Runtime 这个类去做的,但是在 Android Studio 是看不到的,隐藏了,这时候就需要去看系统源码了:http://androidxref.com/9.0.0_r3/xref/libcore/ojluni/src/main/java/java/lang/Runtime.java
System.loadLibrary():
package java.lang;
public class Runtime {
@CallerSensitive
public void loadLibrary(String libname) {
loadLibrary0(VMStack.getCallingClassLoader(), libname); // 拿 classLoader 继续执行逻辑
}
synchronized void loadLibrary0(ClassLoader loader, String libname) {
if (libname.indexOf((int)File.separatorChar) != -1) { // 反斜杠 "\" 符号判断
throw new UnsatisfiedLinkError("Directory separator should not appear in library name: " + libname);
}
String libraryName = libname;
if (loader != null) {
String filename = loader.findLibrary(libraryName); // 找 so 库
if (filename == null) {
throw new UnsatisfiedLinkError(loader + " couldn't find \"" + System.mapLibraryName(libraryName) + "\"");
}
String error = nativeLoad(filename, loader); // 真正加载 so 库的底层函数
if (error != null) { // 加载失败
throw new UnsatisfiedLinkError(error);
}
return;
}
// ....
}
}
可以看到是调用了 classLoader 的 findLibrary() 找到 so 库的路径后传给底层函数进行加载,而这个 classLoader 是 BaseDexClassLoader:
package dalvik.system;
public class BaseDexClassLoader extends ClassLoader {
// ...
private final DexPathList pathList;
@Override
public String findLibrary(String name) {
return pathList.findLibrary(name);
}
// ...
}
又跳到了 DexPathList 的 findLibrary():
package dalvik.system;
final class DexPathList {
// ...
private final List<File> nativeLibraryDirectories; // so 文件集合
public String findLibrary(String libraryName) {
String fileName = System.mapLibraryName(libraryName);
for (NativeLibraryElement element : nativeLibraryPathElements) {
String path = element.findNativeLibrary(fileName); // 找 so 库
if (path != null) {
return path;
}
}
return null;
}
static class NativeLibraryElement {
public String findNativeLibrary(String name) {
// ...
String entryPath = new File(path, name).getPath();
if (IoUtils.canOpenReadOnly(entryPath)) { // 可以打开 so 库(也就是存在这个so)
return entryPath;
}
// ...
return null;
}
}
}
这里描述的是如何去查找是否存在 so 的,就是能否通过 io 流去打开这个 so 路径,若能打开,则说明存在
System.load():
package java.lang;
public class Runtime {
// ...
synchronized void load0(Class<?> fromClass, String filename) {
if (!(new File(filename).isAbsolute())) {
throw new UnsatisfiedLinkError(
"Expecting an absolute path of the library: " + filename);
}
if (filename == null) {
throw new NullPointerException("filename == null");
}
String error = nativeLoad(filename, fromClass.getClassLoader()); // 真正加载 so 库的底层函数
if (error != null) { // 加载失败
throw new UnsatisfiedLinkError(error);
}
}
// ...
}
非常直接,因为传进来的是 so 文件路径,所以直接传给底层函数去加载 so
总结:
System.loadLibrary(String libName) 先把传进来的 so 库名拿去查找对应的路径,查找到 so 文件路径后进行加载。
System.load(String libPath) 根据传进来的 so 文件路径进行加载