JNI so库加载流程之System.loadLibrary流程分析
最近在学习jni相关的知识,很多博客都说,jni***动态注册*时调用System.loadLibrary或者System.load方法加载so库,System.loadLibrary或System.load会调用到so库中的JNI_OnLoad方法进行方法注册,但是这个说是这样说,对于读者依然很模糊,到底System.loadLibrary或System.load到底是怎样的一种流程进行加载的并且调用JNI_OnLoad方法进行注册的呢,为了解开自己的疑惑,在这里看了一Android源码,做一下记录,也希望对别人有所帮助!
下面的代码都来自于android7.1.1_r6源码,除了第一个代码片作为影子其他代码片的第一行都标注了代码片的来源。
System.loadLibrary(libName);
loadLibrary是System.java中的一个静态方法
//libcore/ojluni/src/main/java/java/lang/System.java
public static void loadLibrary(String libname) {
Runtime.getRuntime().loadLibrary0(VMStack.getCallingClassLoader(), libname);
}
getRuntime是Runtime.java中的方法,调用该方法返回Runtime对象,loadLibrary0是Runtime.java中的方法,见下面代码:
//libcore/ojluni/src/main/java/java/lang/Runtime.java
private static Runtime currentRuntime = new Runtime();//单例模式
public static Runtime getRuntime() {
return currentRuntime;
}
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);
}//判断传入的库名称是否合法,比如我们的库是libxxx.so,我们只需要传入xxx就可以了
String libraryName = libname;
if (loader != null) {
//如果类加载器不为空
String filename = loader.findLibrary(libraryName);//查找是否存在我们需要的库文件
if (filename == null) {
// It's not necessarily true that the ClassLoader used
// System.mapLibraryName, but the default setup does, and it's
// misleading to say we didn't find "libMyLibrary.so" when we
// actually searched for "liblibMyLibrary.so.so".
throw new UnsatisfiedLinkError(loader + " couldn't find \"" +
System.mapLibraryName(libraryName) + "\"");
}//不存在库文件则抛出异常
String error = doLoad(filename, loader);//如果库文件存在,就加载
if (error != null) {
throw new UnsatisfiedLinkError(error);
}//加载库文件失败,抛出异常
return;
}
//下面这些代码是类加载器为空的情况下才执行,正常情况下开发者app中开发者自己写的库文件加载时不会执行到这里,因为传入的类加载器不会为空,系统应用才有可能走这里,这时下面获取系统默认的库存放路径才是有用的
String filename = System.mapLibraryName(libraryName);
List<String> candidates = new ArrayList<String>();
String lastError = null;
for (String directory : getLibPaths()) {
//getLibPaths()用来获取系统中存放so库的文件路径,下面有相关的实现代码和解释
String candidate = directory + filename;//拼我们要加载库的绝对路径
candidates.add(candidate);
if (IoUtils.canOpenReadOnly(candidate)) {
//判断绝对路径上的文件是否存在
String error = doLoad(candidate, loader);//如果存在,并且是只可读,则加载该库
if (error == null) {
return; // We successfully loaded the library. Job done.
}//出现错误直接返回
lastError = error;
}
}
if (lastError != null) {
throw new UnsatisfiedLinkError(lastError);
}
throw new UnsatisfiedLinkError("Library " + libraryName + " not found; tried " + candidates);
}
private String[] getLibPaths() {
if (mLibPaths == null) {
synchronized(this) {
if (mLibPaths == null) {
mLibPaths = initLibPaths();//调用initLibPaths方法
}
}
}
return mLibPaths;
}
private static String[] initLibPaths() {
String javaLibraryPath = System.getProperty("java.library.path");//可以看出系统默认的库文件存放路径是在java.library.path属性中存储的
if (javaLibraryPath == null) {
return EmptyArray.STRING;
}
String[] paths = javaLibraryPath.split(":");
// Add a '/' to the end of each directory so we don't have to do it every time.
for (int i = 0; i < paths.length; ++i) {
if (!paths[i].endsWith("/")) {
paths[i] += "/";
}
}
return paths;
}
private String doLoad(String name, ClassLoader loader) {
// Android apps are forked from the zygote, so they can't have a custom LD_LIBRARY_PATH,
// which means that by default an app's shared library directory isn't on LD_LIBRARY_PATH.
// The PathClassLoader set up by frameworks/base knows the appropriate path, so we can load
// libraries with no dependencies just fine, but an app that has multiple libraries that
// depend on each other needed to load them in most-dependent-first order.
// We added API to Android's dynamic linker so we can update the library path used for
// the currently-running process. We pull the desired path out of the ClassLoader here
// and pass it to nativeLoad so that it can call the private dynamic linker API.
// We didn't just change frameworks/base to update the LD_LIBRARY_PATH once at the
// beginning because multiple apks can run in the same process and third party code can
// use its own BaseDexClassLoader.
// We didn't just add a dlopen_with_custom_LD_LIBRARY_PATH call because we wanted any
// dlopen(3) calls made from a .so's JNI_OnLoad to work too.
// So, find out what the native library search path is for the ClassLoader in question...
String librarySearchPath = null;
if (loader != null && loader instanceof BaseDexClassLoader) {
BaseDexClassLoader dexClassLoader = (BaseDexClassLoader) loader;
librarySearchPath = dexClassLoader.getLdLibraryPath();
}
// nativeLoad should be synchronized so there's only one LD_LIBRARY_PATH in use regardless
// of how many ClassLoaders are in the system, but dalvik doesn't support synchronized
// internal natives.
synchronized (this) {
return nativeLoad(name, loader, librarySearchPath);//调用本地方法nativeLoad,nativeLoad是一个本地方法
}
}
// TODO: should be synchronized, but dalvik doesn't support synchronized internal natives.
private