前文提到,AdapterApp里面,一来就用system.loadLibrary(bluetooth_jni)加载JNI库。来看看具体实现。
System.java路径:libcore/luni/src/main/java/java/lang/System.java
Android M是这样的, Android N 都没找到这个路径,鬼晓得在哪点,反正不是我关注的重点。
1072 /**
1073 * See {@link Runtime#loadLibrary}.
1074 */
1075 public static void loadLibrary(String libName) {
1076 Runtime.getRuntime().loadLibrary(libName, VMStack.getCallingClassLoader());
1077 }
简单粗暴,直接丢给Runtime. 后面那个参数,class loader,虽然我不明白,但结合注释“// Returns the defining class loader of the caller’s caller.”,我大胆的猜,算了,最后再来猜,继续看,万一猜错得太远,就不好意思了。
好了,来到了Runtime : libcore/luni/src/main/java/java/lang/Runtime.java
323 /*
324 * Loads the given shared library using the given ClassLoader.
325 */
326 void load(String absolutePath, ClassLoader loader) {
327 if (absolutePath == null) {
328 throw new NullPointerException("absolutePath == null");
329 }
330 String error = doLoad(absolutePath, loader);
331 if (error != null) {
332 throw new UnsatisfiedLinkError(error);
333 }
334 }
423 if (loader == null) {
424 // We use the given library path for the boot class loader. This is the path
425 // also used in loadLibraryName if loader is null.
426 ldLibraryPath = System.getProperty("java.library.path");
427 } else if (loader instanceof BaseDexClassLoader) {
428 BaseDexClassLoader dexClassLoader = (BaseDexClassLoader) loader;
429 ldLibraryPath = dexClassLoader.getLdLibraryPath();
430 }
431 // nativeLoad should be synchronized so there's only one LD_LIBRARY_PATH in use regardless
432 // of how many ClassLoaders are in the system, but dalvik doesn't support synchronized
433 // internal natives.
434 synchronized (this) {
435 return nativeLoad(name, loader, ldLibraryPath);
436 }
通过loader得到 ldLibraryPath参数,看名字,应该是/system/lib,应该是了。
进入到Runtime对应的native 函数:
路径:art/runtime/native/java_lang_Runtime.cc
105static JNINativeMethod gMethods[] = {
106 NATIVE_METHOD(Runtime, freeMemory, "!()J"),
107 NATIVE_METHOD(Runtime, gc, "()V"),
108 NATIVE_METHOD(Runtime, maxMemory, "!()J"),
109 NATIVE_METHOD(Runtime, nativeExit, "(I)V"),
110 NATIVE_METHOD(Runtime, nativeLoad, "(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/String;)Ljava/lang/String;"),
111 NATIVE_METHOD(Runtime, totalMemory, "!()J"),
112};
//下面的宏定义来自于JniConstants.h
99#define NATIVE_METHOD(className, functionName, signature) \
100 { #functionName, signature, reinterpret_cast<void*>(className ## _ ## functionName) }
70static jstring Runtime_nativeLoad(JNIEnv* env, jclass, jstring javaFilename, jobject javaLoader,
71 jstring javaLdLibraryPathJstr) {
72 ScopedUtfChars filename(env, javaFilename);
73 if (filename.c_str() == nullptr) {
74 return nullptr;
75 }
76
77 SetLdLibraryPath(env, javaLdLibraryPathJstr);
78
79 std::string error_msg;
80 {
81 JavaVMExt* vm = Runtime::Current()->GetJavaVM();
82 bool success = vm->LoadNativeLibrary(env, filename.c_str(), javaLoader, &error_msg);
83 if (success) {
84 return nullptr;
85 }
86 }
87
88 // Don't let a pending exception from JNI_OnLoad cause a CheckJNI issue with NewStringUTF.
89 env->ExceptionClear();
90 return env->NewStringUTF(error_msg.c_str());
91}
此处通过JavaVMExt 去真枪实干。为什么是javaVMExt? 为什么不是javaVM?Ext是啥?external、extra?理解不了。
据不完整调查,度娘里面就一个解释,重复了N遍:
JavaVMExt:JavaVM在JNI编程中代表虚拟机本身。在Dalvik中,这个虚拟机本身真正的数据类型是此处的JavaVMExt。
那就是说这个虚拟机在JNI中的一个代表,JavaVM已经被dalvik用了,所以这点用JavaVMExt来表示,如果不出意外的话,Ext应该为External,因为之前大部分的hardware都在external目录下。我猜的。
596bool JavaVMExt::LoadNativeLibrary(JNIEnv* env, const std::string& path, jobject class_loader,
597 std::string* error_msg) {
...
645 void* handle = dlopen(path_str, RTLD_NOW);
646 bool needs_native_bridge = false;
647 if (handle == nullptr) {
648 if (android::NativeBridgeIsSupported(path_str)) {
649 handle = android::NativeBridgeLoadLibrary(path_str, RTLD_NOW);
650 needs_native_bridge = true;
651 }
652 }
...
690 void* sym;
691 if (needs_native_bridge) {
692 library->SetNeedsNativeBridge();
693 sym = library->FindSymbolWithNativeBridge("JNI_OnLoad", nullptr);
694 } else {
695 sym = dlsym(handle, "JNI_OnLoad");
696 }
...
697 if (sym == nullptr) {
698 VLOG(jni) << "[No JNI_OnLoad found in \"" << path << "\"]";
699 was_successful = true;
700 } else {
709 typedef int (*JNI_OnLoadFn)(JavaVM*, void*);
710 JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym);
711 int version = (*jni_on_load)(this, nullptr);
718
719 if (version == JNI_ERR) {
730 } else {
731 was_successful = true;
732 }
735 }
进到LoadNativeLibrary之后,先判断下libpath对不对,然后打开指定的lib库。然后找到以一个叫JNI_onLoad的函数,这也是为什么每个JNI里面的首要函数的JNI_onLoad了,这点就加载到JNI的入口函数了。
问题还有,就是如果sym==nullptr之后,还是要设置was_successful = true?这不是吃多了吗??
好了,就这样吧,到了BT JNI了。
后面再说吧。
对了,说了要猜一下那个loader是啥的。后面有个这样的注释:
604 SharedLibrary* library;
609 library = libraries_->Get(path);
611 if (library != nullptr) {
612 if (env->IsSameObject(library->GetClassLoader(), class_loader) == JNI_FALSE) {
613 // The library will be associated with class_loader. The JNI
614 // spec says we can't load the same library into more than one
615 // class loader.
616 StringAppendF(error_msg, "Shared library \"%s\" already opened by "
617 "ClassLoader %p; can't open in ClassLoader %p",
618 path.c_str(), library->GetClassLoader(), class_loader);
619 LOG(WARNING) << error_msg;
620 return false;
621 }
大致认识为:通过loader去申请内存空间,且所有的动态库都应该是共享同一个loader实例。
还有个疑问就是:按照Android的写法的话,加载lib完成之后,就去判断别个的JNI_onLoader函数,那岂不是如果非JNI的第三方库,就不能使用System.loadLibrary来加载了?
目前我是这么理解,Android不支持loadLibrary来加载第三方库,且不支持第三方loader加载器。
这下这节真结束了