JNI制定了一些規範,支持其他語言調用它的方法。本地方法的變化(實質上是C或C++庫)大於Java代碼的變化。
本地方法調用規範
當虛擬機(VM)(在Android中,即Dalvik)調用C或C++實現的函數時,它會傳遞兩個特殊的參數:
·JNIEnv指針,指向虛擬機中調用本地方法的線程。
·Jobject指針,是調用類的引用。
這些參數會被「透明」地傳遞給Java代碼,也就是說,它們不會出現在聲明調用Java代碼的方法簽名中。Java調用只是顯式傳遞所調用的函數需要的其他任何參數。
JNI方法調用可能看起來如下:
/* sample method where the Java call passed no parameters */ void Java_ClassName_MethodName (JNIEnv *env, jobject obj) { /* do something */ } /* another sample method with two parameters passed, returning a double */ jdouble Java_ClassName_MethodName ( JNIEnv* env, jobject obj, jdouble x, jdouble y) { return x + y; }
這些示例顯示了自動傳遞給每個本地方法的兩個參數,以及匹配Java類型的兩個參數。
當本地方法被調用時,它和調用它的Java代碼在相同的進程和線程中運行。正如我們在本章後面將要看到的,本地方法可以從Java堆中分配內存,以充分利用垃圾回收機制,或在Java堆外面分配內存,以避開Java內存管理。C或C++的代碼中在棧上分配的變量和這些語言的本地變量的語義相同。這些變量也是在它們所在的進程的棧空間中分配的。
JNI提供了和Java類型一致的類型,如表19-1所示。
表19-1:數據映射
表19-1:數據映射(續)
在復合類型中,如對像、數組和字符串等,本地代碼必須顯式調用轉換方法對數據進行轉換,這些轉換方法需要使用JNIEnv指針訪問。
Java端的規範
在Java類中使用本地方法之前,必須使用System.loadLibrary預先加載本地方法所在的庫。通常情況下,要使用本地方法的類會靜態加載這個方法。類中要被本地訪問的方法會使用關鍵字native來聲明。示例代碼如下:
public class ClassWithNativeMethod { public native double nativeMethod; // native method static { System.loadLibrary("sample"); // load lib called 'sample' } public static void main(String args) { ClassWithNativeMethod cwnm = new ClassWithNativeMethod; double answer = cwnm.nativeMethod; // call native method System.out.println("Answer is : "+answer); } }