一般要使用 JNI 技術來實現 Java 應用程序和原生代碼的通訊。java
任何使用JNI的操做都須要兩次或者三次函數調用,所以要實現大量的原生方法並讓它們同Java類保持同步很容易編程一件很是艱辛的工做。編程
而利用一些開源的方案則能夠幫助咱們基於現有的原生代碼接口自動生成 JNI 的代碼。數組
在Java代碼中使用native關鍵字能夠聲明原生方法,例如:多線程
public native String stringFromJNI();app
注意只是聲明而已,「()」這對括弧後面直接就是分號了。函數
static { System.loadLibrary("hello-jni");//libhello-jni.so }學習
之因此要在靜態代碼塊中調用 System.loadLibrary ,就是爲了Java類首次加載和初始化時就能讓原生代碼的實現被載入。spa
原生代碼是C/C++的,所以要有一個頭文件明確要實現那些方法,JDK爲咱們提供了javah(頭文件生成器)來根據已經編譯好的class生成頭文件,例如:線程
javah –classpath bin/classes com.example.hellojni.HelloJni指針
javah 能夠配置到 Eclipse 的 External Tools 中,方便使用。
Java代碼中對原生方法的聲明能夠不帶上參數,但對應的原生函數式帶有參數的,例如:
JNIEXPORT jstring JNICALL Java_com_example_hellojni_HelloJni_stringFromJNI (JNIEnv *, jobject);
JNIEnv 是一個指針,指向可供JNI使用的函數列表。JNIEnv所指向的類型根據語言不一樣(C/C++)而不一樣,若是是C,則其所指向的類型就是 JNINativeInterface 結構,若是是C++,則是一個擁有成員方法的類實例。
若是要返回一個 String ,C的寫法是:
return (*env)->NewStringUTF(env, "Hello from JNI !");//由於C中的JNI函數不清楚當前的JNI環境,因此要傳入env。
而C++的寫法則是:
return env->NewStringUTF("Hello from JNI !");
jobject 是一個引用了 HelloJni 類的實例的 Java 對象。
原生實例方法的定義示例以下:
JNIEXPORT jstring JNICALL Java_com_example_hellojni_HelloJni_stringFromJNI (JNIEnv * env, jobject thiz);
原生靜態方法的定義示例以下:
JNIEXPORT jstring JNICALL Java_com_example_hellojni_HelloJni_stringFromJNI (JNIEnv * env, jclass clazz);
固然是分爲了原生類型和引用類型。
原生類型在Java , JNI 和 C/C++中有各自的對應的表示方法,引用類型也是。
新建字符串:
jstring javaString; javaString = (*env)->NewStringUTF(env, "Hello World!");
Java 字符串轉成 C 字符串: const jbyte* str; jboolean isCopy; str = (*env)->GetStringUTFChars(env, javaString, &isCopy); if (0 != str) { printf("Java string: %s", str); if (JNI_TRUE == isCopy) { printf("C string is a copy of the Java string."); } else { printf("C string points to actual string."); }
新建數組:
jintArray javaArray; javaArray = (env)->NewIntArray(env, 10); if (0 != javaArray) { / You can now use the array. */ }
經過操做副原本訪問數組元素:
jint nativeArray[10]; (*env)->GetIntArrayRegion(env, javaArray, 0, 10, nativeArray);
將在 C 數組上的修改提交到 Java Array 中:
(*env)->SetIntArrayRegion(env, javaArray, 0, 10, nativeArray);
直接在指針上對數組進行操做:
jint* nativeDirectArray; jboolean isCopy; nativeDirectArray = (*env)->GetIntArrayElements(env, javaArray, &isCopy);
新建:
unsigned char* buffer = (unsigned char*) malloc(1024); ... jobject directBuffer; directBuffer = (*env)->NewDirectByteBuffer(env, buffer, 1024);
獲取:
unsigned char* buffer; buffer = (unsigned char*) (*env)->GetDirectBufferAddress(env, directBuffer);
public class JavaClass { // Instance field private String instanceField = "Instance Field"; // Static field private static String staticField = "Static Field"; ... }
獲取域ID:
jclass clazz; clazz = (*env)->GetObjectClass(env, instance);
jfieldID instanceFieldId; instanceFieldId = (*env)->GetFieldID(env, clazz, "instanceField", "Ljava/lang/String;");
獲取靜態域ID:
jstring staticField; staticField = (*env)->GetStaticObjectField(env, clazz, staticFieldId);
public class JavaClass { //Instance method. private String instanceMethod() { return "Instance Method"; } //Static method. private static String staticMethod() { return "Static Method"; } ... }
獲取方法ID:
jmethodID instanceMethodId; instanceMethodId = (*env)->GetMethodID(env, clazz, "instanceMethod", "()Ljava/lang/String;");
獲取靜態方法ID:
jmethodID staticMethodId; staticMethodId = (*env)->GetStaticMethodID(env, clazz, "staticMethod", "()Ljava/lang/String;");
調用方法:
jstring instanceMethodResult; instanceMethodResult = (*env)->CallStringMethod(env, instance, instanceMethodId);
調用靜態方法:
jstring staticMethodResult; staticMethodResult = (*env)->CallStaticStringMethod(env, clazz, staticMethodId);
Java Type Signature Boolean Z Byte B Char C Short S Int I Long J Float F Double D fully-qualified-class Lfully-qualified-class; type[] [type method type (arg-type)ret-type
使用 javap 能夠從 class 文件中提取出域和方法的描述符:
javap –classpath bin/classes –p –s com.example.hellojni.HelloJni
public class JavaClass { //Throwing method. private void throwingMethod() throws NullPointerException { throw new NullPointerException("Null pointer"); } //Access methods native method. private native void accessMethods(); }
原生代碼是這麼寫的:
jthrowable ex; ... (*env)->CallVoidMethod(env, instance, throwingMethodId); ex = (*env)->ExceptionOccurred(env); if (0 != ex) { (*env)->ExceptionClear(env); //Exception handler. }
jclass clazz; ... clazz = (*env)->FindClass(env, "java/lang/NullPointerException"); if (0 ! = clazz) { (*env)->ThrowNew(env, clazz, "Exception message."); }
jclass clazz; clazz = (*env)->FindClass(env, "java/lang/String");
新增全局引用:
jclass localClazz; jclass globalClazz; ... localClazz = (*env)->FindClass(env, "java/lang/String"); globalClazz = (*env)->NewGlobalRef(env, localClazz); ... (*env)->DeleteLocalRef(env, localClazz);
刪除全局引用:
(*env)->DeleteGlobalRef(env, globalClazz);
新增全局弱引用:
jclass weakGlobalClazz; weakGlobalClazz = (*env)->NewWeakGlobalRef(env, localClazz);
驗證全局弱引用是否可用:
if (JNI_FALSE == (*env)->IsSameObject(env, weakGlobalClazz, NULL)) { //Object is still live and can be used. } else { //Object is garbage collected and cannot be used. }
刪除全局弱引用:
(*env)->DeleteWeakGlobalRef(env, weakGlobalClazz);
Java代碼是這樣寫的:
synchronized(obj) { //Synchronized thread-safe code block. }
原生代碼應該這樣寫:
if (JNI_OK == (*env)->MonitorEnter(env, obj)) { //Error handling. } //Synchronized thread-safe code block. if (JNI_OK == (*env)->MonitorExit(env, obj)) { // Error handling. }
將當前線程同虛擬機綁定和解綁: JavaVM* cachedJvm; ... JNIEnv* env; ... //Attach the current thread to virtual machine. (*cachedJvm)->AttachCurrentThread(cachedJvm, &env, NULL); //Thread can communicate with the Java application using the JNIEnv interface. //Detach the current thread from virtual machine. (*cachedJvm)->DetachCurrentThread(cachedJvm);