Android JNI/Hardware 加载(二)

前文提到,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加载器。

这下这节真结束了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值