📄 ch05.html
字号:
<A NAME="pgfId-1087841"></A><EM CLASS="Bold">3. Call the Methods. </EM><A NAME="marker-1087840"></A>Third, the matching instance method is called using a <EM CLASS="CODE">Call<type>Method</EM> function. The <EM CLASS="CODE">type</EM> value can be <EM CLASS="CODE">Void</EM>, <EM CLASS="CODE">Object</EM>, <EM CLASS="CODE">Boolean</EM>, <EM CLASS="CODE">Byte</EM>, <EM CLASS="CODE">Char</EM>, <EM CLASS="CODE">Short</EM>, <EM CLASS="CODE">Int</EM>, <EM CLASS="CODE">Long</EM>, <EM CLASS="CODE">Float</EM>, or <EM CLASS="CODE">Double</EM>. </P><P CLASS="Body"><A NAME="pgfId-1087842"></A>The parameters to the method can be passed as a comma-separated list, an array of values to the <EM CLASS="CODE">Call<type>MethodA</EM> function, or as a <EM CLASS="CODE">va_list</EM>. The <EM CLASS="CODE">va_list</EM> is a construct often used for variable argument lists in C. <EM CLASS="CODE">Call<type>MethodV</EM> is the function used to pass a <EM CLASS="CODE">va_list()</EM>. </P><P CLASS="Body"><A NAME="pgfId-1087843"></A>Static methods are called in a similar way except the method naming includes an additional <EM CLASS="CODE">Static</EM> identifier, <EM CLASS="CODE">CallStaticByteMethodA</EM>, and the <EM CLASS="CODE">jclass</EM> value is used instead of <EM CLASS="CODE">jobject</EM>. </P><P CLASS="Body"><A NAME="pgfId-1087844"></A>The next example returns the object array by calling the <EM CLASS="CODE">sendArrayResults</EM> method from the <EM CLASS="CODE">ArrayHandler</EM> class. </P><PRE CLASS="CODE"><A NAME="pgfId-1087845"></A>// ArrayHandler.javapublic class ArrayHandler { private String arrayResults[]; int arraySize=-1; public native void returnArray(); static{</PRE><PRE CLASS="CODE-caption"><A NAME="pgfId-1087847"></A>//API Ref: <A NAME="73662"></A>static void loadLibrary(String libraryname)</PRE><PRE CLASS="CODE"><A NAME="pgfId-1087848"></A> System.loadLibrary("nativelib"); } public void sendArrayResults(String results[]) { arraySize=results.length; arrayResults=new String[arraySize]; System.arraycopy(results,0,arrayResults,0,arraySize); } public void displayArray() { for (int i=0; i<arraySize; i++) { System.out.println("array element "+i+ "= " + arrayResults[i]); } } public static void main(String args[]) { String ar[]; ArrayHandler ah= new ArrayHandler(); ah.returnArray(); ah.displayArray(); }</PRE><PRE CLASS="CODE"><A NAME="pgfId-1087849"></A>}</PRE><P CLASS="Body"><A NAME="pgfId-1087850"></A>The native C++ code is defined as follows: </P><PRE CLASS="CODE"><A NAME="pgfId-1087851"></A>//file: nativelib.cc</PRE><PRE CLASS="CODE"><A NAME="pgfId-1087852"></A>#include <jni.h>#include <iostream.h>#include "ArrayHandler.h"JNIEXPORT void JNICALL Java_ArrayHandler_returnArray(JNIEnv *env, jobject jobj){ jobjectArray ret; int i; jclass cls; jmethodID mid; char *message[5]= {"first", "second", "third", "fourth", "fifth"};</PRE><PRE CLASS="CODE-caption"><A NAME="pgfId-1087854"></A>//API Ref: <A NAME="85113"></A>jarray NewObjectArray(JNIEnv *env, jsize length, jclass elementClass, jobject initialElement)</PRE><PRE CLASS="CODE"><A NAME="pgfId-1087857"></A> <A NAME="marker-1087855"></A><A NAME="NewObjectArray function"></A>ret=(jobjectArray)env->NewObjectArray(5, env->FindClass("java/lang/String"), env->NewStringUTF("")); for(i=0;i<5;i++) { env->SetObjectArrayElement(ret,i,env->NewStringUTF(message[i])); } cls=env->GetObjectClass(jobj); mid=env->GetMethodID(cls, "sendArrayResults", "([Ljava/lang/String;)V"); if (mid == 0) { cout <<"Can't find method sendArrayResults"; return; } env->ExceptionClear(); env->CallVoidMethod(jobj, mid, ret); if(env->ExceptionOccurred()) { cout << "error occured copying array back" <<endl; env->ExceptionDescribe(); env->ExceptionClear(); } return;</PRE><PRE CLASS="CODE"><A NAME="pgfId-1087858"></A>}</PRE><P CLASS="Body"><A NAME="pgfId-1087859"></A>To build this on Linux, run the following commands: </P><PRE CLASS="CODE"><A NAME="pgfId-1087860"></A>javac ArrayHandler.javajavah -jni ArrayHandlerg++ -o libnativelib.so -shared -Wl,-soname,libnative.so -I/export/home/jdk1.2/include -I/export/home/jdk1.2/include/linux nativelib.cc -lc</PRE><P CLASS="Body"><A NAME="pgfId-1087861"></A>If you want to specify a super class method to, for example, call the parent constructor, you can do so by calling the <EM CLASS="CODE">CallNonvirtual<type>Method</EM> functions. One important point when calling Java methods or fields from within native code is that you need to catch any raised exceptions. The <EM CLASS="CODE">ExceptionClear</EM><A NAME="marker-1087862"></A> function clears any pending exceptions while the <EM CLASS="CODE">ExceptionOccured</EM><A NAME="marker-1087863"></A> function checks to see if an exception has been raised in the current JNI session. </P></DIV></DIV><DIV><H5 CLASS="B"><A NAME="pgfId-1087864"></A>Accessing Fields</H5><P CLASS="Body"><A NAME="pgfId-1087866"></A><A NAME="marker-1087865"></A>Accessing Java fields from within native code is similar to calling Java methods. However, the set or field is retrieved with a field ID, instead of a method ID. </P><P CLASS="Body"><A NAME="pgfId-1087867"></A>The first thing you need to do is retrieve a field ID. You can use the <EM CLASS="CODE">GetFieldID</EM> function, but specify the field name and signature in place of the method name and signature. Once you have the field ID, call a <EM CLASS="CODE">Get<type>Field</EM><A NAME="marker-1087868"></A> function to set the field value. The <EM CLASS="CODE"><type></EM> is the same as the native type being returned except the <EM CLASS="CODE">j</EM> is dropped and the first letter is capitalized. For example, the <EM CLASS="CODE"><type></EM> value is <EM CLASS="CODE">Int</EM> for native type <EM CLASS="CODE">jint</EM>, and <EM CLASS="CODE">Byte</EM> for native type <EM CLASS="CODE">jbyte</EM>. </P><P CLASS="Body"><A NAME="pgfId-1087869"></A>The <EM CLASS="CODE">Get<type>Field</EM> function result is returned as the native type. For example, to retrieve the <EM CLASS="CODE">arraySize</EM> field in the <EM CLASS="CODE">ArrayHandler</EM> class, call <EM CLASS="CODE">GetIntField</EM> as shown in the following example. </P><P CLASS="Body"><A NAME="pgfId-1087871"></A>The field can be set by calling the <EM CLASS="CODE">env->SetIntField(jobj, fid, arraysize)</EM><A NAME="marker-1087870"></A> functions. Static fields can be set by calling <EM CLASS="CODE">SetStaticIntField(jclass, fid,</EM> <EM CLASS="CODE">arraysize)</EM> and retrieved by calling <EM CLASS="CODE">GetStaticIntField(jclass,</EM> <EM CLASS="CODE">fid)</EM>. </P><PRE CLASS="CODE"><A NAME="pgfId-1087872"></A>#include <jni.h>#include <iostream.h>#include "ArrayHandler.h"JNIEXPORT void JNICALL Java_ArrayHandler_returnArray(JNIEnv *env, jobject jobj){ jobjectArray ret; int i; jint arraysize; jclass cls; jmethodID mid; jfieldID fid; char *message[5]= {"first", "second", "third", "fourth", "fifth"}; ret=(jobjectArray)env->NewObjectArray(5, env->FindClass("java/lang/String"), env->NewStringUTF("")); for(i=0;i<5;i++) { env->SetObjectArrayElement(ret,i,env->NewStringUTF(message[i])); } cls=env->GetObjectClass(jobj); mid=env->GetMethodID(cls, "sendArrayResults", "([Ljava/lang/String;)V"); if (mid == 0) { cout <<"Can't find method sendArrayResults"; return; } env->ExceptionClear(); env->CallVoidMethod(jobj, mid, ret); if(env->ExceptionOccurred()) { cout << "error occured copying array back" << endl; env->ExceptionDescribe(); env->ExceptionClear(); } fid=env->GetFieldID(cls, "arraySize", "I"); if (fid == 0) { cout <<"Can't find field arraySize"; return; } arraysize=env->GetIntField(jobj, fid); if(!env->ExceptionOccurred()) { cout<< "size=" << arraysize << endl; } else { env->ExceptionClear(); } return;}</PRE></DIV><DIV><H5 CLASS="B"><A NAME="pgfId-1087873"></A>Threads and Synchronization</H5><P CLASS="Body"><A NAME="pgfId-1087878"></A><A NAME="marker-1087874"></A><A NAME="marker-1087875"></A><A NAME="marker-1087876"></A><A NAME="marker-1087877"></A>Although the native library is loaded once per class, individual threads in an application written in the Java programming language use their own interface pointer when calling the native method. If you need to restrict access to a Java object from within native code, you can either ensure that the Java methods you call have explicit synchronization or you can use the JNI <EM CLASS="CODE">MonitorEnter</EM> and <EM CLASS="CODE">MonitorExit</EM> functions. </P><P CLASS="Body"><A NAME="pgfId-1087880"></A><A NAME="marker-1087879"></A>In the Java programming language, code is protected by a monitor whenever you specify the <EM CLASS="CODE">synchronized</EM> keyword, and the monitor enter and exit routines are normally hidden from the application developer. In JNI, you need to explicitly delineate the entry and exit points of thread safe code. </P><P CLASS="Body"><A NAME="pgfId-1087881"></A>The following example uses a Boolean object to restrict access to the <EM CLASS="CODE">CallVoidMethod</EM> function. </P><PRE CLASS="CODE"><A NAME="pgfId-1087882"></A>env->ExceptionClear();env->MonitorEnter(lock);env->CallVoidMethod(jobj, mid, ret);env->MonitorExit(lock);if(env->ExceptionOccurred()) { cout << "error occured copying array back" << endl; env->ExceptionDescribe(); env->ExceptionClear();}</PRE><P CLASS="Body"><A NAME="pgfId-1087883"></A>You may find that in cases where you want access to a local system resource like a Microsoft Foundation Classes (MFC) window handle or message queue, it is better to use one <EM CLASS="CODE">Java.lang.Thread</EM> and access the local threaded native event queue or messaging system from within the native code. <EM CLASS="A"></EM><A NAME="memory"></A></P></DIV><DIV><H5 CLASS="B"><A NAME="pgfId-1087886"></A><A NAME="51465"></A>Memory Issues</H5><P CLASS="Body"><A NAME="pgfId-1087888"></A><A NAME="marker-1087887"></A>By default, JNI uses local references when creating objects inside a native method. This means that when the method returns, the references are eligible to be garbage collected. If you want an object to persist across native method calls, use a global reference instead. A global reference is created from a local reference by calling <EM CLASS="CODE">NewGlobalReference</EM><A NAME="marker-1087889"></A> on the local reference. </P><P CLASS="Body"><A NAME="pgfId-1087894"></A><A NAME="marker-1087890"></A>You can explicitly mark a reference for <A NAME="marker-1087891"></A><A NAME="marker-1087892"></A>garbage collection by calling <EM CLASS="CODE">DeleteGlobalRef</EM><A NAME="marker-1087893"></A> on the reference. You can also create a weak style <EM CLASS="CODE">Global</EM> reference that is accessible outside the method but that can be garbage collected. To create one of these references, call <EM CLASS="CODE">NewWeakGlobalRef</EM><A NAME="marker-1087895"></A> and <EM CLASS="CODE">DeleteWeakGlobalRef</EM><A NAME="marker-1087896"></A> to mark the reference for garbage collection. </P><P CLASS="Body"><A NAME="pgfId-1087897"></A>You can even explicitly mark a local refer
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -