Android动态库的加载system.loadlibary(三)
System.loadlibary,System.load是怎么加载so库的?他们是在Runtime的启动过程中通过loadnativelibary来加载libjavacore.so的动态库,这个动态库是这两个函数的实现体。
libcore/ojluni/src/main/java/java/lang/System.java
public static void loadLibrary(String libname) { Runtime.getRuntime().loadLibrary0(VMStack.getCallingClassLoader(), libname); }
直接调用了Runtime的函数loadLibary0,然后经过doload,nativeload进入native层,
libcore/ojluni/src/main/native/runtime.c
JNIEXPORT jstring JNICALL Runtime_nativeLoad(JNIEnv* env, jclass ignored, jstring javaFilename, jobject javaLoader, jstring javaLibrarySearchPath) { return JVM_NativeLoad(env, javaFilename, javaLoader, javaLibrarySearchPath); }
接着通过jvm的LoadNativeLibary执行实际工作,
art/runtime/openjdkjvm/openjdkjvm.cc
JNIEXPORT jstring JVM_NativeLoad(JNIEnv* env, jstring javaFilename, jobject javaLoader, jstring javaLibrarySearchPath) { ScopedUtfChars filename(env, javaFilename); std::string error_msg; { art::JavaVMExt* vm = art::Runtime::Current()->GetJavaVM(); bool success = vm->LoadNativeLibrary(env, filename.c_str(), javaLoader, javaLibrarySearchPath, &error_msg); } // Don't let a pending exception from JNI_OnLoad cause a CheckJNI issue with NewStringUTF. env->ExceptionClear(); return env->NewStringUTF(error_msg.c_str()); }
art/runtime/java_vm_ext.cc
bool JavaVMExt::LoadNativeLibrary(JNIEnv* env, const std::string& path, jobject class_loader, jstring library_path, std::string* error_msg) { error_msg->clear(); // See if we've already loaded this library. If we have, and the class loader // matches, return successfully without doing anything. // TODO: for better results we should canonicalize the pathname (or even compare // inodes). This implementation is fine if everybody is using System.loadLibrary. SharedLibrary* library; Thread* self = Thread::Current(); { // TODO: move the locking (and more of this logic) into Libraries. MutexLock mu(self, *Locks::jni_libraries_lock_); //判断是否已经加载过这个libary library = libraries_->Get(path); } ....... // Open the shared library. Because we're using a full path, the system // doesn't have to search through LD_LIBRARY_PATH. (It may do so to // resolve this library's dependencies though.) // Failures here are expected when java.library.path has several entries // and we have to hunt for the lib. // Below we dlopen but there is no paired dlclose, this would be necessary if we supported // class unloading. Libraries will only be unloaded when the reference count (incremented by // dlopen) becomes zero from dlclose. Locks::mutator_lock_->AssertNotHeld(self); const char* path_str = path.empty() ? nullptr : path.c_str(); void* handle = android::OpenNativeLibrary(env, runtime_->GetTargetSdkVersion(), path_str, class_loader, library_path); //是否需要nativebridge的帮助,如果handle为空指针,说明前面OpenNativeLibrary失败了。 bool needs_native_bridge = false; if (handle == nullptr) { if (android::NativeBridgeIsSupported(path_str)) { handle = android::NativeBridgeLoadLibrary(path_str, RTLD_NOW); needs_native_bridge = true; } } ? // Create a new entry. // TODO: move the locking (and more of this logic) into Libraries. bool created_library = false; { // Create SharedLibrary ahead of taking the libraries lock to maintain lock ordering. std::unique_ptr new_library( new SharedLibrary(env, self, path, handle, 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; } } bool was_successful = false; void* sym; if (needs_native_bridge) { library->SetNeedsNativeBridge(); } sym = library->FindSymbol("JNI_OnLoad", nullptr); if (sym == nullptr) { VLOG(jni) << "[No JNI_OnLoad found in \"" << path << "\"]"; was_successful = true; } else { // Call JNI_OnLoad. We have to override the current class // loader, which will always be "null" since the stuff at the // top of the stack is around Runtime.loadLibrary(). (See // the comments in the JNI FindClass function.) ScopedLocalRef old_class_loader(env, env->NewLocalRef(self->GetClassLoaderOverride())); self->SetClassLoaderOverride(class_loader); } library->SetResult(was_successful); return was_successful; }
LoadNativeLibrary的目的就是通过各种手段去加载动态链接库,然后执行其中的JNI_OnLoad接口(这个函数是jni库的首选入口,可以利用它完成一些初始化工作)。
上面函数中有个NativeBridge的概念:这个技术用来处理当前系统平台的指令集和目标对象(Library)的指令集不一致时的兼容性问题。比如说,Android的模拟器,通常直接编译成x86版本运行在开发机器上,那么对于一个只提供了arm版本的动态链接库文件的apk,如何保证它可以在x86模拟器上运行,就是nativeBridge要解决的问题。
正常途径,加载Library利用的是OpenNativeLibrary这个函数。
system/core/libnativeloader/native_loader.cpp
void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path, jobject class_loader, jstring library_path) { #if defined(__ANDROID__) UNUSED(target_sdk_version); if (class_loader == nullptr) { return dlopen(path, RTLD_NOW); } std::lock_guard guard(g_namespaces_mutex); android_namespace_t* ns = g_namespaces->FindNamespaceByClassLoader(env, class_loader); if (ns == nullptr) { // This is the case where the classloader was not created by ApplicationLoaders // In this case we create an isolated not-shared namespace for it. ns = g_namespaces->Create(env, class_loader, false, library_path, nullptr); if (ns == nullptr) { return nullptr; } } android_dlextinfo extinfo; extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE; extinfo.library_namespace = ns; return android_dlopen_ext(path, RTLD_NOW, &extinfo); #else UNUSED(env, target_sdk_version, class_loader, library_path); return dlopen(path, RTLD_NOW); #endif }
先利用FindNamespaceByClassLoader查找当前的ClassLoader是否有相关的Namespace,如果没有直接跳转到android_dlopen_ext;如果有调用其Create方法创建一个Namespace。
android_dlopen_ext跟dlopen类似,第一个参数是要打开的动态库的名称,第二个参数RTLD_NOW,表示动态库中所有未定义的符号在dlopen返回前都会被解析。
接下来的实现,是调用find_libary来查找动态库,找到后,调用dlsym来查找加载的动态库中是否包含JNI_OnLoader入口函数。
接下来看下Java层声明的native函数如何与本地层的具体实现建立其正确的映射关系。
在动态库的入口函数JNI_OnLoader中,通常会调用AndroidRuntime::registerNativeMethods方法:
int ret1 = AndroidRuntime::registerNativeMethods(env, "android/media/ImageReader", gImageReaderMethods, NELEM(gImageReaderMethods));
registerNativeMethods就是把JNI中的函数注册到虚拟机中,这个函数是对JNIRegisterNativeMethods的中转。
libnativehelper/JNIHelp.cpp
extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className, const JNINativeMethod* gMethods, int numMethods) { JNIEnv* e = reinterpret_cast(env); scoped_local_ref c(env, findClass(env, className)); if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) { char* tmp; const char* msg; if (asprintf(&tmp, "RegisterNatives failed for '%s'; aborting...", className) == -1) { // Allocation failed, print default warning. msg = "RegisterNatives failed; aborting..."; } else { msg = tmp; } e->FatalError(msg); } return 0; }
变量env是JNIEnv对象,是由JNI_OnLoader的JavaVM获取的,并且是线程私有的。
art/runtime/jni_internal.cc
static jint RegisterNatives(JNIEnv* env, jclass java_class, const JNINativeMethod* methods, jint method_count) { return RegisterNativeMethods(env, java_class, methods, method_count, true); }
static jint RegisterNativeMethods(JNIEnv* env, jclass java_class, const JNINativeMethod* methods, jint method_count, bool return_errors) { if (UNLIKELY(method_count < 0)) { JavaVmExtFromEnv(env)->JniAbortF("RegisterNatives", "negative method count: %d", method_count); return JNI_ERR; // Not reached except in unit tests. } CHECK_NON_NULL_ARGUMENT_FN_NAME("RegisterNatives", java_class, JNI_ERR); ScopedObjectAccess soa(env); mirror::Class* c = soa.Decode(java_class); if (UNLIKELY(method_count == 0)) { LOG(WARNING) << "JNI RegisterNativeMethods: attempt to register 0 native methods for " << PrettyDescriptor(c); return JNI_OK; } CHECK_NON_NULL_ARGUMENT_FN_NAME("RegisterNatives", methods, JNI_ERR); for (jint i = 0; i < method_count; ++i) { const char* name = methods[i].name; const char* sig = methods[i].signature; const void* fnPtr = methods[i].fnPtr; if (UNLIKELY(name == nullptr)) { ReportInvalidJNINativeMethod(soa, c, "method name", i, return_errors); return JNI_ERR; } else if (UNLIKELY(sig == nullptr)) { ReportInvalidJNINativeMethod(soa, c, "method signature", i, return_errors); return JNI_ERR; } else if (UNLIKELY(fnPtr == nullptr)) { ReportInvalidJNINativeMethod(soa, c, "native function", i, return_errors); return JNI_ERR; } bool is_fast = false; // Notes about fast JNI calls: // // On a normal JNI call, the calling thread usually transitions // from the kRunnable state to the kNative state. But if the // called native function needs to access any Java object, it // will have to transition back to the kRunnable state. // // There is a cost to this double transition. For a JNI call // that should be quick, this cost may dominate the call cost. // // On a fast JNI call, the calling thread avoids this double // transition by not transitioning from kRunnable to kNative and // stays in the kRunnable state. // // There are risks to using a fast JNI call because it can delay // a response to a thread suspension request which is typically // used for a GC root scanning, etc. If a fast JNI call takes a // long time, it could cause longer thread suspension latency // and GC pauses. // // Thus, fast JNI should be used with care. It should be used // for a JNI call that takes a short amount of time (eg. no // long-running loop) and does not block (eg. no locks, I/O, // etc.) // // A '!' prefix in the signature in the JNINativeMethod // indicates that it's a fast JNI call and the runtime omits the // thread state transition from kRunnable to kNative at the // entry. if (*sig == '!') { is_fast = true; ++sig; } // Note: the right order is to try to find the method locally // first, either as a direct or a virtual method. Then move to // the parent. ArtMethod* m = nullptr; bool warn_on_going_to_parent = down_cast(env)->vm->IsCheckJniEnabled(); for (mirror::Class* current_class = c; current_class != nullptr; current_class = current_class->GetSuperClass()) { // Search first only comparing methods which are native. m = FindMethod(current_class, name, sig); if (m != nullptr) { break; } // Search again comparing to all methods, to find non-native methods that match. m = FindMethod(current_class, name, sig); if (m != nullptr) { break; } if (warn_on_going_to_parent) { LOG(WARNING) << "CheckJNI: method to register \"" << name << "\" not in the given class. " << "This is slow, consider changing your RegisterNatives calls."; warn_on_going_to_parent = false; } } if (m == nullptr) { LOG(return_errors ? ERROR : INTERNAL_FATAL) << "Failed to register native method " << PrettyDescriptor(c) << "." << name << sig << " in " << c->GetDexCache()->GetLocation()->ToModifiedUtf8(); // Safe to pass in LOG(FATAL) since the log object aborts in destructor and only goes // out of scope after the DumpClass is done executing. c->DumpClass(LOG(return_errors ? ERROR : FATAL), mirror::Class::kDumpClassFullDetail); ThrowNoSuchMethodError(soa, c, name, sig, "static or non-static"); return JNI_ERR; } else if (!m->IsNative()) { LOG(return_errors ? ERROR : FATAL) << "Failed to register non-native method " << PrettyDescriptor(c) << "." << name << sig << " as native"; ThrowNoSuchMethodError(soa, c, name, sig, "native"); return JNI_ERR; } VLOG(jni) << "[Registering JNI native method " << PrettyMethod(m) << "]"; m->RegisterNative(fnPtr, is_fast); } return JNI_OK; }
注册过程的目的是建立Java函数和native函数之间的对应关系,所以先通过FindMethod找到Java_class中的Java层函数,这个注册通常是一个函数集合methods,集合中的每一个元素都是JNINativeMethod对象,包含了java函数名、signature、native函数原型。FindMethod查找到的结果是一个ArtMethod,最后有RegisterNative完成注册工作,它要做的就是将native method在内存中地址通过SetEntryPointFromJni设置为对应ArtMethod的JNI入口。在JNI环境下,当java层的函数被调用后,首先找到它的ArtMethod对象,然后通过GetEntryPoint得到JNI入口代码的地址。
这就完成了java层跟native层函数的JNI映射。
推荐阅读
-
使用数据库客户端工具Oracle SQL Developer加载第三方驱动连接mysql的方法
-
PHP通过反射动态加载第三方类和获得类源码的实例
-
Android常用的图片加载库
-
web前端从Oracle数据库加载动态菜单所用到的数据表
-
Android开发实现的ViewPager引导页功能(动态加载指示器)详解
-
Android动态库的加载system.loadlibary(三)
-
Android中解决第三方库重复引用的问题
-
如何将ffmpeg x264的动态库编译入Android7.1系统源码(详细步骤)
-
使用android studio开发工具编译GBK转换三方库iconv的方法
-
JavaScript开发中动态加载jquery库的方法