Preface
This article describes the principle of System.loadLibrary.
Java layer source code flow
System#loadLibrary
-> Runtime#loadLibrary0
–> BaseDexClassLoader#findLibrary
–> DexPathList#findNativeLibrary
->Runtime#nativeLoad,走向c层源码
- Mainly from the
nativeLibraryPathElements
find in so path, through all the paths found so exist, does not exist is thrownUnsatisfiedLinkError
. Open the so logic in the c layer. If the c layer returns false, the error string will be spliced, and the Java layer will also throw it after receiving itUnsatisfiedLinkError
.
- You can print nativeLibraryPathElements through reflection to better understand this piece of code
Field pathListF = BaseDexClassLoader.class.getDeclaredField("pathList");
pathListF.setAccessible(true);
ClassLoader dexPathClassLoader = TestSocketFragment.class.getClassLoader();
Object pathList = pathListF.get(dexPathClassLoader);
Field nativeLibraryPathElementsF = Class.forName("dalvik.system.DexPathList").getDeclaredField("nativeLibraryPathElements");
nativeLibraryPathElementsF.setAccessible(true);
Object nativeLibraryPathElements = nativeLibraryPathElementsF.get(pathList);
android.util.Log.e("mLogU", nativeLibraryPathElements.toString());
Object[] arr = (Object[]) nativeLibraryPathElements;
for (Object o : arr) {
android.util.Log.e("mLogU", o.toString());
}
Native layer source code process
Load so from the c layer and return false will be thrown in the Java layer UnsatisfiedLinkError
. The process is as follows:
-> nativeLoad
->libcore/ojluni/src/main/native/Runtime.c#Runtime_nativeLoad
->art/openjdkjvm/OpenjdkJvm.cc#JVM_NativeLoad
->art/runtime/jni/java_vm_ext.cc#LoadNativeLibrary
–>art/libnativeloader/native_loader.cpp#OpenNativeLibrary
bool JavaVMExt::LoadNativeLibrary(JNIEnv* env,
const std::string& path,
jobject class_loader,
std::string* error_msg) {
error_msg->clear();
/*
* 1. 从缓存中获取该library,
* 2. 如果存在则比较classLoader是否相同,如果相同则返回成功。不同则返回失败。
* 3. 不存在则进行加载,并放入缓存中
*/
SharedLibrary* library;
Thread* self = Thread::Current();
{
MutexLock mu(self, *Locks::jni_libraries_lock_);
library = libraries_->Get(path);
}
void* class_loader_allocator = nullptr;
{
ScopedObjectAccess soa(env);
ObjPtr<mirror::ClassLoader> loader = soa.Decode<mirror::ClassLoader>(class_loader);
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
if (class_linker->IsBootClassLoader(soa, loader.Ptr())) {
loader = nullptr;
class_loader = nullptr;
}
// 获取当前classloader的Allocator
class_loader_allocator = class_linker->GetAllocatorForClassLoader(loader.Ptr());
CHECK(class_loader_allocator != nullptr);
}
/*
* The JNI spec says we can't load the same library into more than one class loader.
* JNI规范要求不能加载相同库到同一个classLoader中
*/
if (library != nullptr) {
// Use the allocator pointers for class loader equality to avoid unnecessary weak root decode.
if (library->GetClassLoaderAllocator() != class_loader_allocator) {
// .... 拼接一大堆str
return false;
}
// 已经加载过了,问题不大
if (!library->CheckOnLoadResult()) {
return false;
}
return true;
}
// dlopen该库,并返回句柄(指针)
void* handle = android::OpenNativeLibrary(env,
runtime_->GetTargetSdkVersion(),
path_str,
class_loader,
library_path.get(),
&needs_native_bridge,
error_msg);
if (handle == nullptr) {
// 打开失败
return false;
}
bool created_library = false;
{
std::unique_ptr<SharedLibrary> new_library(
new SharedLibrary(env,
self,
path,
handle,
needs_native_bridge,
class_loader,
class_loader_allocator));
MutexLock mu(self, *Locks::jni_libraries_lock_);
library = libraries_->Get(path);
if (library == nullptr) {
// We won race to get libraries_lock.
library = new_library.release();
// 放入缓存
libraries_->Put(path, library);
created_library = true;
}
}
if (!created_library) {
return library->CheckOnLoadResult();
}
/*
* 加载成功,dlsym获取JNI_OnLoad句柄,并调用JNI_OnLoad方法,根据返回值判断设置是否加载成功
*/
bool was_successful = false;
void* sym = library->FindSymbol("JNI_OnLoad", nullptr);
if (sym == nullptr) {
was_successful = true;
} else {
JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym);
int version = (*jni_on_load)(this, nullptr);
if (version == JNI_ERR) {
StringAppendF(error_msg, "JNI_ERR returned from JNI_OnLoad in \"%s\"", path.c_str());
} else if (JavaVMExt::IsBadJniVersion(version)) {
StringAppendF(error_msg, "Bad JNI version returned from JNI_OnLoad in \"%s\": %d",
path.c_str(), version);
} else {
was_successful = true;
}
}
library->SetResult(was_successful);
return was_successful;
}
Conclusion
It can be seen from the source code that System#loadLibrary will only call the JNI_OnLoad
method once under normal circumstances ;
among them, SharedLibrary will call dlclose in the destructor to close the library;
and from the official document JNI tips, it can be seen that it is generally placed in a static code block to load so , The so life cycle is bound to the class life cycle.
This article sorts out the system#loadLibrary source code process, please correct me if there are any errors.