通报和利用Java工具
在前例中,我们将一个字勾串报给固有要领。事实上,亦可将本身建设的Java工具通报给固有要领。
在我们的固有要领内部,可会见已收到的那些工具的字段及要领。
为通报工具,声明固有要领时要回收原始的Java语法。如下例所示,MyJavaClass有一个public(民众)字段,以及一个public要领。UseObjects类声明白一个固有要领,用于吸收MyJavaClass类的一个工具。为观测固有要领是否能节制本身的自变量,我们配置了自变量的public字段,挪用固有要领,然后打印出public字段的值。
class MyJavaClass { public void divByTwo() { aValue /= 2; } public int aValue; } public class UseObjects { public static void main(String [] args) { UseObjects app = new UseObjects(); MyJavaClass anObj = new MyJavaClass(); anObj.aValue = 2; app.changeObject(anObj); System.out.println("Java: " + anObj.aValue); } private native void changeObject(MyJavaClass obj); static { System.loadLibrary("UseObjImpl"); } }
编译好代码,并将.class文件通报给javah后,就可以实现固有要领。在下面这个例子中,一旦取得字段和要领ID,就会通过JNI函数会见它们。
JNIEXPORT void JNICALL Java_UseObjects_changeObject( JNIEnv * env, jobject jThis, jobject obj) { jclass cls; jfieldID fid; jmethodID mid; int value; cls = env->GetObjectClass(obj); fid = env->GetFieldID(cls, "aValue", "I"); mid = env->GetMethodID(cls, "divByTwo", "()V"); value = env->GetIntField(obj, fid); printf("Native: %d\n", value); env->SetIntField(obj, fid, 6); env->CallVoidMethod(obj, mid); value = env->GetIntField(obj, fid); printf("Native: %d\n", value); }
除第一个自变量外,C++函数会吸收一个jobject,它代表Java工具引用“固有”的那一面——谁人引用是我们从Java代码里通报的。我们简朴地读取aValue,把它打印出来,改变这个值,挪用工具的divByTwo()要领,再将值从头打印一遍。
为会见一个字段或要领,首先必需获取它的标识符。操作适当的JNI函数,可利便地取得类工具、元素名以及签名信息。这些函数会返回一个标识符,操作它可会见对应的元素。尽量这一方法显得有些曲折,但我们的固有要领确实对Java工具的内部机关一无所知。因此,它必需通过由JVM返回的索引会见字段和要领。这样一来,差异的JVM就可实现差异的内部工具机关,同时不会对固有要领造成影响。
若运行Java措施,就会发明从Java那一侧传来的工具是由我们的固有要领处理惩罚的。但通报的到底是什么呢?是指针,照旧Java引用?并且垃圾收集器在固有要领挪用期间又在做什么呢?
垃圾收集器会在固有要领执行期间一连运行,但在一次固有要领挪用期间,我们的工具可担保不会被看成“垃圾”收集去。为确保这一点,事先建设了“局部引用”,并在固有要领挪用之后当即排除。由于它们的“生命期”与挪用进程息息相关,所以可以或许担保工具在固有要领挪用期间的有效性。
由于这些引用会在每次函数挪用的时候建设和粉碎,所以不行在static变量中建造固有要领的局部副本(当地拷贝)。若但愿一个引用在函数存在期间一连有效,就需要一个全局引用。全局引用不是由JVM建设的,但通过挪用特定的JNI函数,措施员可将局部引用扩展为全局引用。建设一个全局引用时,需对引用工具的“保留时间”认真。全局引用(以及它引用的工具)会一直留在内存里,直到用特定的JNI函数明晰释放了这个引用。它雷同于C的malloc()和free()。