<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                [TOC] # JNI基礎 上面一節主要描述了系統中Java層和Native層交互和實現,并沒有對JNI的基礎理論,流程進行分析 ## JNI命名規則 JNI方法名規范 : ~~~ 返回值 + Java前綴 + 全路徑類名 + 方法名 + 參數① JNIEnv + 參數② jobject + 其它參數 ~~~ 簡單的一個例子,返回一個字符串 ~~~ extern "C" JNIEXPORT jstring JNICALL Java_com_yeungeek_jnisample_NativeHelper_stringFromJNI(JNIEnv *env, jclass jclass1) { LOGD("##### from c"); return env->NewStringUTF("Hello JNI"); } ~~~ * 返回值:jstring * 全路徑類名:com\_yeungeek\_jnisample\_NativeHelper * 方法名:stringFromJNI ## JNI開發流程 * 在Java中先聲明一個native方法 * 編譯Java源文件javac得到.class文件 * 通過javah -jni命令導出JNI的.h頭文件 * 使用Java需要交互的本地代碼,實現在Java中聲明的Native方法(如果Java需要與C++交互,那么就用C++實現Java的Native方法。) * 將本地代碼編譯成動態庫(Windows系統下是.dll文件,如果是Linux系統下是.so文件,如果是Mac系統下是.jnilib) * 通過Java命令執行Java程序,最終實現Java調用本地代碼。 ## 數據類型 ### 基本數據類型 | Signature | Java | Native | | --- | --- | --- | | B | byte | jbyte | | C | char | jchar | | D | double | jdouble | | F | float | jfloat | | I | int | jint | | S | short | jshort | | J | long | jlong | | Z | boolean | jboolean | | V | void | jvoid | ### 引用數據類型 | Signature | Java | Native | | --- | --- | --- | | L+classname +; | Object | jobject | | Ljava/lang/String; | String | jstring | | \[L+classname +; | Object\[\] | jobjectArray | | Ljava.lang.Class; | Class | jclass | | Ljava.lang.Throwable; | Throwable | jthrowable | | \[B | byte\[\] | jbyteArray | | \[C | char\[\] | jcharArray | | \[D | double\[\] | jdoubleArray | | \[F | float\[\] | jfloatArray | | \[I | int\[\] | jintArray | | \[S | short\[\] | jshortArray | | \[J | long\[\] | jlongArray | | \[Z | boolean\[\] | jbooleanArray | ## 方法簽名 JNI的方法簽名的格式: ~~~ (參數簽名格式...)返回值簽名格式 復制代碼 ~~~ demo的native 方法: ~~~ public static native java.lang.String stringFromJNI(); 復制代碼 ~~~ 可以通過javap命令生成方法簽名``: ~~~ ()Ljava/lang/String; 復制代碼 ~~~ # JNI原理 Java語言的執行環境是Java虛擬機(JVM),JVM其實是主機環境中的一個進程,每個JVM虛擬機都在本地環境中有一個JavaVM結構體,該結構體在創建Java虛擬機時被返回,在JNI環境中創建JVM的函數為JNI\_CreateJavaVM。 JNI 定義了兩個關鍵數據結構,即“JavaVM”和“JNIEnv”,兩者本質上都是指向函數表的二級指針。 ## JavaVM JavaVM是Java虛擬機在JNI層的代表,JavaVM 提供了“調用接口”函數,您可以利用此類函數創建和銷毀 JavaVM。理論上,每個進程可以包含多個JavaVM,但AnAndroid只允許每個進程包含一個JavaVM。 ## JNIEnv JNIEnv是一個線程相關的結構體,該結構體代表了Java在本線程的執行環境。JNIEnv 提供了大多數 JNI 函數。您的原生函數均會接收 JNIEnv 作為第一個參數。 JNIEnv作用: * 調用Java函數 * 操作Java代碼 JNIEnv定義(jni.h): `libnativehelper/include/nativehelper/jni.h` ~~~ #if defined(__cplusplus) typedef _JNIEnv JNIEnv; typedef _JavaVM JavaVM; #else typedef const struct JNINativeInterface* JNIEnv; typedef const struct JNIInvokeInterface* JavaVM; #endif 復制代碼 ~~~ 定義中可以看到JavaVM,Android中一個進程只會有一個JavaVM,一個JVM對應一個JavaVM結構,而一個JVM中可能創建多個Java線程,每個線程對應一個JNIEnv結構 ![](https://img.kancloud.cn/18/93/1893602172709cde07c48970ba284cc5_834x449.png) ## 注冊JNI函數 Java世界和Native世界的方法是如何關聯的,就是通過JNI函數注冊來實現。JNI函數注冊有兩種方式: ### 靜態注冊 這種方法就是通過函數名來找對應的JNI函數,可以通過`javah`命令行來生成JNI頭文件 ~~~ javah com.yeungeek.jnisample.NativeHelper 復制代碼 ~~~ 生成對應的`com_yeungeek_jnisample_NativeHelper.h`文件,生成對應的JNI函數,然后在實現這個函數就可以了 ~~~ /* * Class: com_yeungeek_jnisample_NativeHelper * Method: stringFromJNI * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_yeungeek_jnisample_NativeHelper_stringFromJNI (JNIEnv *, jclass); 復制代碼 ~~~ 靜態注冊方法中,Native是如何找到對應的JNI函數,在[JNI查找方式](#JNI%E6%9F%A5%E6%89%BE%E6%96%B9%E5%BC%8F "#JNI%E6%9F%A5%E6%89%BE%E6%96%B9%E5%BC%8F")中介紹系統的流程,并沒有詳細說明靜態注冊的查找。這里簡單說明下這個過程(以上面的聲明為例子s): 當Java調用native stringFromJNI函數時,會從對應JNI庫中查找`Java_com_yeungeek_jnisample_NativeHelper_stringFromJNI`函數,如果沒有找到,就會報錯。 靜態注冊方法,就是根據函數名來關聯Java函數和JNI函數,JNI函數需要遵循特定的格式,這其中就有一些缺點: * 聲明了native方法的Java類,需要通過`javah`來生成頭文件 * JNI函數名稱非常長 * 第一次調用native函數,需要通過函數名來搜索關聯對應的JNI函數,效率比較低 如何解決這些問題,讓native函數,提前知道JNI函數,就可以解決這個問題,這個過程就是動態注冊。 ### 動態注冊 動態注冊在前面的Camera例子中,已經有涉及到,JNI函數`classInit`的聲明。 ~~~ static const JNINativeMethod gCameraMetadataMethods[] = { // static methods { "nativeClassInit", "()V", (void *)CameraMetadata_classInit }, //和Java層nativeClassInit()對應 ...... } 復制代碼 ~~~ JNI中有一種結構用來記錄Java的Native方法和JNI方法的關聯關系,它就是JNINativeMethod,它在jni.h中被定義: ~~~ typedef struct { const char* name; //Java層native函數名 const char* signature; //Java函數簽名,記錄參數類型和個數,以及返回值類型 void* fnPtr; //Native層對應的函數指針 } JNINativeMethod; 復制代碼 ~~~ 在[JNI查找方式](#JNI%E6%9F%A5%E6%89%BE%E6%96%B9%E5%BC%8F "#JNI%E6%9F%A5%E6%89%BE%E6%96%B9%E5%BC%8F")說到,JNI注冊的兩種時間,第一種已經介紹過了,我們自定義的native函數,基本都是會使用`System.loadLibrary(“xxx”)`,來進行JNI函數的關聯。 #### loadLibrary(Android7.0) ~~~ public static void loadLibrary(String libname) { Runtime.getRuntime().loadLibrary0(VMStack.getCallingClassLoader(), libname); } 復制代碼 ~~~ 調用到Runtime(libcore/ojluni/src/main/java/java/lang/Runtime.java)的loadLibrary0方法: ~~~ synchronized void loadLibrary0(ClassLoader loader, String libname) { ...... 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) + "\""); } //doLoad String error = doLoad(filename, loader); if (error != null) { throw new UnsatisfiedLinkError(error); } return; } //loader 為 null ...... for (String directory : getLibPaths()) { 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; } } ...... } 復制代碼 ~~~ #### doLoad ~~~ private String doLoad(String name, ClassLoader loader) { //調用 native 方法 synchronized (this) { return nativeLoad(name, loader, librarySearchPath); } } 復制代碼 ~~~ #### nativeLoad 進入到虛擬機代碼`/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_NativeLoad`,JVM\_NativeLoad方法申明在jvm.h中,實現在`OpenjdkJvm.cc(/art/runtime/openjdkjvm/OpenjdkJvm.cc)` ~~~ JNIEXPORT jstring JVM_NativeLoad(JNIEnv* env, jstring javaFilename, jobject javaLoader, jstring javaLibrarySearchPath) { ScopedUtfChars filename(env, javaFilename); if (filename.c_str() == NULL) { return NULL; } std::string error_msg; { art::JavaVMExt* vm = art::Runtime::Current()->GetJavaVM(); bool success = vm->LoadNativeLibrary(env, filename.c_str(), javaLoader, javaLibrarySearchPath, &error_msg); if (success) { return nullptr; } } // Don't let a pending exception from JNI_OnLoad cause a CheckJNI issue with NewStringUTF. env->ExceptionClear(); return env->NewStringUTF(error_msg.c_str()); } 復制代碼 ~~~ #### LoadNativeLibrary 調用JavaVMExt的LoadNativeLibrary方法,方法在(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) { ...... bool was_successful = false; //加載so庫中查找JNI_OnLoad方法,如果沒有系統就認為是靜態注冊方式進行的,直接返回true,代表so庫加載成功, //如果找到JNI_OnLoad就會調用JNI_OnLoad方法,JNI_OnLoad方法中一般存放的是方法注冊的函數, //所以如果采用動態注冊就必須要實現JNI_OnLoad方法,否則調用java中申明的native方法時會拋出異常 void* 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<jobject> old_class_loader(env, env->NewLocalRef(self->GetClassLoaderOverride())); self->SetClassLoaderOverride(class_loader); VLOG(jni) << "[Calling JNI_OnLoad in \"" << path << "\"]"; typedef int (*JNI_OnLoadFn)(JavaVM*, void*); JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym); //調用JNI_OnLoad方法 int version = (*jni_on_load)(this, nullptr); if (runtime_->GetTargetSdkVersion() != 0 && runtime_->GetTargetSdkVersion() <= 21) { // Make sure that sigchain owns SIGSEGV. EnsureFrontOfChain(SIGSEGV); } self->SetClassLoaderOverride(old_class_loader.get()); } ...... } 復制代碼 ~~~ 代碼里的主要邏輯: * 加載so庫中查找JNI\_OnLoad方法,如果沒有系統就認為是靜態注冊方式進行的,直接返回true,代表so庫加載成功 * 如果找到JNI\_OnLoad就會調用JNI\_OnLoad方法,JNI\_OnLoad方法中一般存放的是方法注冊的函數 * 所以如果采用動態注冊就必須要實現`JNI_OnLoad`方法,否則調用Java中的native方法時會拋出異常 ## jclass、jmethodID和jfieldID 如果要通過原生代碼訪問對象的字段,需要執行以下操作: 1. 使用 FindClass 獲取類的類對象引用 2. 使用 GetFieldID 獲取字段的字段 ID 3. 使用適當內容獲取字段的內容,例如 GetIntField 具體的使用,放在第二篇文章中講解 ## JNI的引用 JNI規范中定義了三種引用: * 局部引用(Local Reference) * 全局引用(Global Reference) * 弱全局引用(Weak Global Reference) ### 局部引用 也叫本地引用,在 JNI層函數使用的非全局引用對象都是Local Reference,最大的特點就是,JNI 函數返回后,這些聲明的引用可能就會被垃圾回收 ### 全局引用 這種聲明的對象,不會主動釋放資源,不會被垃圾回收 ### 弱全局引用 一種特殊的全局引用,在運行過程中可能被回收,使用之前需要判斷下是否為空 # 參考資料 [Android NDK-深入理解JNI](https://juejin.cn/post/6844903933375152136)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看