Java中JNI的使用(上)

JNI 全稱是 Java Native Interface。是在 Java 和 Native 層(包括但不限於C/C++)相互調用的接口規範。java

JNI 在 Java 1.1中正式推出,在 Java 1.2版本中加入了 JNI_OnLoad、JNI_OnUnload 方法,這兩個方法仍是頗有用的,後面再說。面試

JNI基礎篇數組

Java 經過 JNI 調用本地方法的過程大體是:架構

寫一個 Java 類,在其中聲明對應要調用的 native 方法,用 native 關鍵字修飾。 好比 private static native int native_newInstance(); 經過 javah 命令生成 Java 類對應的 C/C++ 頭文件。javah -encoding utf-8 -cp src com.young.soundtouch.SoundTouch; 在 C/C++ 中實現頭文件中聲明的函數; 編譯 C/C++ 代碼爲動態庫(Windows中的dll、Linux/Android 中的 so、MAC OSX 中的 dylib); 在 Java 代碼中加載動態庫,便可像調用 Java 方法同樣,調用到 native 函數。 其中第3步在 Java 1.2 中增長了 JNI_OnLoad 方法以後有另外一種實現方式(後面說)。框架

javah 生成的頭文件大體是這樣的:分佈式

/* DO NOT EDIT THIS FILE - it is machine generated / #include <jni.h> / Header for class com_young_soundtouch_SoundTouch */函數

#ifndef _Included_com_young_soundtouch_SoundTouch #define _Included_com_young_soundtouch_SoundTouch #ifdef __cplusplus extern "C" { #endif #undef com_young_soundtouch_SoundTouch_SETTING_USE_AA_FILTER #define com_young_soundtouch_SoundTouch_SETTING_USE_AA_FILTER 0L /*源碼分析

  • Class: com_young_soundtouch_SoundTouch
  • Method: native_getDefaultSampleElementSize
  • Signature: ()I */ JNIEXPORT jint JNICALL Java_com_young_soundtouch_SoundTouch_native_1getDefaultSampleElementSize (JNIEnv *, jclass); #ifdef __cplusplus } #endif #endif 文件開頭就是普通的頭文件,可是能夠發現:

包含了 jni.h 頭文件(通常位於 $JAVA_HOME/jd{jdk-version}/include 文目錄內)。這是 JNI 中全部的類型、函數、宏等定義的地方。因此C/C++世界的JNI是由他制定的遊戲規則。 在類中生命的常量(static final)類型會在頭文件中以宏的形式出現,這一點仍是很方便的。 函數的註釋仍是比較全的,包括了: 對應的 class 對應的 Java 方法名 對應 Java 方法的簽名 方法的聲明顯得有點奇怪,由如下及部分組成: JNIEXPORT這是函數的導出方式; jint 返回值類型(jint 由 jni.h 定義,對應 int,下面具體再說吧); JNICALL 函數的調用方式也就是彙編級別參數的傳入方式; Java_com_young_soundtouch_SoundTouch_native_1getDefaultSampleElementSize —— 超級長的函數名!!!格式是 Java_ + 類全名 + _ + JAVA 中聲明的native方法名。其中會把包名中的點(.)替換成下劃線(_),同時爲了不衝突把下劃線替換成_1; 方法的參數,上面的這個方法在 Java 的聲明中其實是沒有參數的,其中的 JNIENV 顧名思義是 JNI 環境,和具體的線程綁定。而第二個參數 jclass 實際上是 Java 中的 Class。由於上面是一個 static 方法,所以第二個參數是 jclass。若是是一個實例方法則對應第二個參數是 jobject,至關於 Java中的 this。 下面在 C/C++ 中實現這個方法就行啦。可是在動手前現大體瞭解如下 jni.h 制定的遊戲規則。性能

類型轉換學習

javah 生成的頭文件裏面使用的類型都是 jni.h 定義的,目的是作到平臺無關,好比保證在全部平臺上 jint 都是32位的有符號整型。

基本對應關係以下:

引用類型對應關係:

經過表格發現,除了上面定義的 String、Class、Throwable,其餘的類(除了數組)都是以 jobject 的形式出現的!事實上 jstring、 jclass 也都是 object 的子類。因此這裏仍是和 Java 層同樣,一切皆 jobject。(固然,若是 jni 在 C 語言中編譯的話是沒有繼承的概念的,此時 jstring、jclass 等其實就是 jobject!用了 typedef 轉換而已!!)

接下來是 JNIEnv * 這個指針,它提供了 JNI 中的一系列操做的接口函數。

JNI 中操做 jobject

其實也就是在native層操做 Java 層的實例。 要操做一個實例無疑是:

獲取/設置 (即 get/set )成員變量(field)的值; 調用成員方法(method)。 因此問題來了:(挖掘機技術哪家強?! o(*≧▽≦)ツ┏━┓ )

怎麼獲得 field 和 method?

經過使用 jfieldID 和 jmethodID: 在 JNI 中使用相似於放射的方式來進行 field 和 method 的操做。JNI 中使用j fieldID 和 jmethodID 來表示成員變量和成員方法,獲取方式是:

jfieldID GetFieldID(jclass clazz, const char *name, const char *sig); jfieldID GetStaticFieldID(jclass clazz, const char *name, const char *sig); jmethodID GetMethodID(jclass clazz, const char *name, const char *sig); jmethodID GetStaticMethodID(jclass clazz, const char *name, const char *sig) ; 其中最後一個參數是簽名。 獲取jclass的方法除了實用上面靜態方法的第二個參數外,還能夠手動獲取。 jclass FindClass(const char *name) 須要注意的是name參數,他是一個類包括包名的全稱,可是須要把包名中的點.替換成斜槓/。(好吧,事實上我不是太明白爲啥要這麼作。)

有了 jfieldID 和 jmethodID 就知道狗蛋住哪了,如今去狗蛋家找他玩 ♪(^∇^*)

  1. get:

GetField(jobject , jfieldID); 便可得到對應的field,其中field的類型是type,能夠是上面類型所敘述的任何一種 GetStaticField(jobject , jfieldID); 同1,惟一的區別是用來獲取靜態成員。 2. set:

void SetField(jobject obj, jfieldID fieldID, val) void SetStaticField(jclass clazz, jfieldID fieldID, value); 成員方法:

調用方法天然要把方法的參數傳遞進去,JNI中實現了三種參數的傳遞方式:

CallMethod(jobject obj, jmethod jmethodID, ...) 其中 ... 是 C 中的可變長參數,相似於 printf 那樣,能夠傳遞不定長個參數。因而你能夠把 Java 方法須要的參數在這裏面傳遞進去。 CallMethodV(jobject obj, jmethodID methodID, va_list args) 其中的 va_list 也是 C 中可變長參數相關的內容(我不瞭解,不敢瞎說,偷懶粘一下Oracle的文檔)「Programmers place all arguments to the method in an args argument of type va_list that immediately follows the methodID argument. The CallMethodV routine accepts the arguments, and, in turn, passes them to the Java method that the programmer wishes to invoke.」 CallMethodA(jobject obj, jmethodID methodID, const jvalue * args) 哎!這個我知道能夠說兩句 LOL ~~這裏的 jvalue 經過查代碼發現就是 JNI 中各個數據類型的 union,因此可使用任何類型複製!因此參數的傳入方式是經過一個 jvalue 的數組,數組內的元素能夠是任何 jni 類型。 而後問題又來了:(挖掘機技術到底哪家強?!o(*≧▽≦)ツ┏━┓) 若是傳進來的參數和java聲明的參數的不一致會怎麼樣!(即不符合方法簽名)這裏文檔中沒用明確解釋,可是說道: > Exceptions raised during the execution of the Java method.

typedef union jvalue { jboolean z; jbyte b; jchar c; jshort s; jint i; jlong j; jfloat f; jdouble d; jobject l; } jvalue;

  1. 調用實例方法(instance method):

CallMethod(jobject obj, jmethodID methodID, ...); 調用一個具備類型返回值的方法。 CallMethodV(jobject obj, jmethodID methodID, va_list args); CallMethodA(jobject obj, jmethodID methodID, const jvalue * args) 2. 調用靜態方法(static method):

CallStaticMethod(jobject obj, jmethodID methodID, ...); CallStaticMethodV(jobject obj, jmethodID methodID, va_list args); CallStaticMethodA(jobject obj, jmethodID methodID, const jvalue * args) 3. 調用父類方法(super.method),這個就有點不同了。多了一個 jclass 參數,jclass 可使 obj 的父類,也能夠是 obj 本身的class,可是 methodID 必須是從 jclass 獲取到的,這樣就能夠調用到父類的方法。

CallNonvirtualMethod(jobject obj, jclass clazz, jmethodID methodID, ...) CallNonvirtualMethodV(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, va_list args); CallNonvirtualMethodA(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, const jvalue *args);

在這裏給你們提供一個學習交流的平臺,java架構師羣: 867748702

具備1-5工做經驗的,面對目前流行的技術不知從何下手,須要突破技術瓶頸的能夠加羣。

在公司待久了,過得很安逸,但跳槽時面試碰壁。須要在短期內進修、跳槽拿高薪的能夠加羣。

若是沒有工做經驗,但基礎很是紮實,對java工做機制,經常使用設計思想,經常使用java開發框架掌握熟練的能夠加羣。


加Java架構師進階交流羣獲取Java工程化、高性能及分佈式、高性能、深刻淺出。高架構。 性能調優、Spring,MyBatis,Netty源碼分析和大數據等多個知識點高級進階乾貨的直播免費學習權限 都是大牛帶飛 讓你少走不少的彎路的 羣號是: 867748702對了 小白勿進 最好是有開發經驗

注:加羣要求

一、具備工做經驗的,面對目前流行的技術不知從何下手,須要突破技術瓶頸的能夠加。

二、在公司待久了,過得很安逸,但跳槽時面試碰壁。須要在短期內進修、跳槽拿高薪的能夠加。

三、若是沒有工做經驗,但基礎很是紮實,對java工做機制,經常使用設計思想,經常使用java開發框架掌握熟練的,能夠加。

四、以爲本身很牛B,通常需求都能搞定。可是所學的知識點沒有系統化,很難在技術領域繼續突破的能夠加。

5.阿里Java高級大牛直播講解知識點,分享知識,多年工做經驗的梳理和總結,帶着你們全面、科學地創建本身的技術體系和技術認知!

相關文章
相關標籤/搜索