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); }}