Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 77 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
77
Dung lượng
1,2 MB
Nội dung
column in the table. The [NativeType] is replaced with the exact name of the native data type from col- umn three in the table. For example, to create a new integer array, you use the function NewIntArray that returns jintArray. Name of Primitive Array Type( Primitive Type Data Type (For Use in C/C++ Code) (For Use in C/C++ Code) boolean jbooleanArray jboolean byte jbyteArray jbyte char jcharArray jchar short jshortArray jshort int jintArray jint long jlongArray jlong float jfloatArray jfloat double jdoubleArray jdouble The NewArray function returns a newly created Java array that is length elements in size: [ArrayType] New[Type]Array(jsize length); The GetArrayElements function returns a pointer to an array of the native type that corresponds to the Java data type: [NativeType] *Get[Type]ArrayElements([ArrayType] array, jboolean *isCopy); The parameter isCopy is set to JNI_TRUE if the memory returned is a copy of the array from the Java code, or JNI_FALSE if the memory is not a copy. The ReleaseArrayElements function releases the memory obtained from the call to Get[Type]ArrayElements: void Release[Type]ArrayElements([ArrayType] array, [NativeType] *elems, jint mode); If the native array is not a copy, the mode parameter can be used to optionally copy memory from the native array back to the Java array. The values of mode and their effects are listed in the following table. Value of Mode Description 0 Copies the memory from the native array to the Java array and deallocates the memory used by the native array. JNI_COMMIT Copies the memory from the native array to the Java array, but does not deallocate the memory used by the native array. JNI_ABORT Does not copy memory from the native array to the Java array. The memory used by the native array is still deallocated. 438 Part II: A Broad Understanding of Java APIs, Tools, and Techniques 14_777106 ch09.qxp 11/28/06 10:36 PM Page 438 The GetArrayRegion function operates much like Get[Type]ArrayElements. However, this is used to copy only a subset of the array: void Get[Type]ArrayRegion([ArrayType] array, jsize start, jsize len, [NativeType] *buf); The parameter start specifies the starting index to copy from, and len specifies how many positions in the array to copy into the native array. The SetArrayRegion is the counterpart to Get[Type]Array Region . This function is used to copy a segment of a native array back to the Java array: void Set[Type]ArrayRegion([ArrayType] array, jsize start, jsize len, [NativeType] *buf); Elements are copied directly from the beginning of the native array (index 0) but are copied into the Java array starting at position start and len elements are copied over. The GetPrimitiveArrayCritical function returns a handle to an array after obtaining a lock on the array: void *GetPrimitiveArrayCritical(jarray array, jboolean *isCopy); If no lock could be established, the isCopy parameter comes back with a value JNI_TRUE. Otherwise, isCopy comes back NULL or as JNI_FALSE. The ReleasePrimitiveArrayCritical releases the array previously returned from a call to GetPrimitiveArrayCritical: void ReleasePrimitiveArrayCritical(jarray array, void *carray, jint mode); Look at the next table to see how the mode parameter affects the array and carray parameters. Value for Mode Meaning 0 Copies the values from carray into array and frees the memory associated with carray. JNI_COMMIT Copies the values from carray into array but does not free the memory associated with carray. JNI_ABORT Does not copy the values from carray to array, but does free the memory associated with carray. Here’s an example of implementing a sort routine in native code. To keep things simple, the insertion sort is used. The Java code, as usual, is fairly simple. The native method is declared, then the library is statically loaded, and the native method is invoked in the main method: public class PrimitiveArrayExample { public native boolean sortIntArray(int[] numbers); static { System.loadLibrary(“PrimitiveArrayLibrary”); } public static void main(String args[]) { 439 Chapter 9: Interacting with C/C++ Using Java Native Interface 14_777106 ch09.qxp 11/28/06 10:36 PM Page 439 PrimitiveArrayExample pae = new PrimitiveArrayExample(); int numberList[] = {4, 1, 2, 20, 11, 7, 2}; if(pae.sortIntArray(numberList)) { System.out.print(“The sorted numbers are: “); for(int i=0; i<numberList.length; i++) { System.out.print(numberList[i] + “ “); } System.out.println(); } else { System.out.println(“The sort operation failed because “ + “the array memory could not be allocated.”); } } } The native code uses the array functions to work with an array of integers: JNIEXPORT jboolean JNICALL Java_PrimitiveArrayExample_sortIntArray (JNIEnv *env, jobject obj, jintArray intArrayToSort) { jint *intArray; jboolean isCopy; int i, j, num; intArray = env->GetIntArrayElements(intArrayToSort, &isCopy); if(intArray == NULL) { return(JNI_FALSE); } for(i=1; i<env->GetArrayLength(intArrayToSort); i++) { num = intArray[i]; for(j=i-1; j >= 0 && (intArray[j] > num); j ) { intArray[j+1] = intArray[j]; } intArray[j+1] = num; } env->ReleaseIntArrayElements(intArrayToSort, intArray, 0); return(JNI_TRUE); } This sortIntArray function uses the GetIntArrayElements in order to work with the array in a native form. The GetArrayLength function is used to know how many elements are in the array, and finally, ReleaseIntArrayElements is used to both save the changed memory to the Java array and deallocate the memory. As one final example of arrays, create an array of strings and then implement a find function that returns the index to the string: 440 Part II: A Broad Understanding of Java APIs, Tools, and Techniques 14_777106 ch09.qxp 11/28/06 10:36 PM Page 440 public class ObjectArrayExample { public native int findString(String[] stringList, String stringToFind); static { System.loadLibrary(“ObjectArrayLibrary”); } public static void main(String args[]) { ObjectArrayExample oae = new ObjectArrayExample(); String[] colors = {“red”,”blue”,”black”,”green”,”grey”}; int foundIndex; System.out.println(“Searching for ‘black’ ”); foundIndex = oae.findString(colors, “black”); if(foundIndex != -1) { System.out.println(“The color ‘black’ was found at index “ + foundIndex); } else { System.out.println(“The color ‘black’ was not found”); } } } An array of strings is created and passed to the native method findString. If the string is not found, the method returns -1 and otherwise returns the index to the string from the array: JNIEXPORT jint JNICALL Java_ObjectArrayExample_findString (JNIEnv *env, jobject obj, jobjectArray strList, jstring strToFind) { const char *findStr; int i; int arrayLen; arrayLen = env->GetArrayLength(strList); findStr = env->GetStringChars(strToFind, NULL); if(findStr == NULL) { return(-1); } for(i=0; i<arrayLen; i++) { jstring strElem = (jstring)env->GetObjectArrayElement(strList, i); if(strElem != NULL) { const char *strTemp = env->GetStringChars(strElem, NULL); if(strcmp(strTemp, findStr) == 0) { env->ReleaseStringChars(strElem, strTemp); env->ReleaseStringChars(strToFind, findStr); env->DeleteLocalRef(strElem); 441 Chapter 9: Interacting with C/C++ Using Java Native Interface 14_777106 ch09.qxp 11/28/06 10:36 PM Page 441 break; } env->ReleaseStringChars(strElem, strTemp); env->DeleteLocalRef(strElem); } env->ReleaseStringChars(strToFind, findStr); } if(i == arrayLen) { return(-1); } else { return(i); } } The GetArrayLength function is used to retrieve the length of the object array. The object array is then accessed using the GetObjectArrayElement function to retrieve a specific element. Note that the object is then cast to a jstring in order to get a handle to the array element’s specific type. Also note that because the GetObjectArrayElement function returns a local reference, the reference is freed using DeleteLocalRef. As is explained in the local reference section, this call to DeleteLocalRef isn’t neces- sary in this case, but it introduces the fact that many native functions return a local reference that should be cleaned up. Working with Java Objects in C/C++ Java Native Interface also provides a set of functions to manipulate Java objects (using methods/fields), handle exceptions, and synchronize data for threads. These functions provide greater access to Java objects on the native side, allowing for more sophisticated applications. One way that these functions can be used is to make callbacks to Java methods, perhaps to communicate information. You see this in action in the mail client example at the end of this chapter. Accessing Fields in JNI There are two types of member variables in Java classes — static fields, which belong to classes, and non- static fields, which belong to individual objects. In order to gain access to a field, you must pass a field descriptor and the name of the field to GetFieldID or GetStaticFieldID. A field descriptor is one or more characters that fully describe a field’s type. For example, the field int number has as its field descriptor I. Consult the next table for a full list of descriptors for primitive types. The descriptor for an array type is prefixed with the character [ for each dimension of the array. Therefore, the type int[] numbers is described by [I, and int[][] numbers is [[I. For reference types, the fully qualified name of the class is used but the dots are replaced with a forward slash and the descriptor is surrounded by an L at the beginning and a semicolon at the end. For example, the type java.lang.Integer is described by Ljava/lang/Integer;. 442 Part II: A Broad Understanding of Java APIs, Tools, and Techniques 14_777106 ch09.qxp 11/28/06 10:36 PM Page 442 Primitive Type (Java) Field Descriptor boolean Z byte B char C short S int I long J float F double D Much like the variety of functions for use with arrays of primitive types, each primitive type has its own Get and Set function for fields. This section also uses the abbreviated version for compactness. The [NativeType] is replaced by a string from the first column of the next table, and [Type] is replaced by the corresponding string from the second column in the table. Name of Primitive Data Type (Java) Primitive Type (For Use in C/C++ Code) boolean jboolean byte jbyte char jchar short jshort int jint long jlong float jfloat double jdouble Here are the functions that are provided to access fields inside Java classes: jfieldID GetFieldID(jclass clazz, const char *name, const char *sig); The GetFieldID function returns a handle to the specified field for use in the Get and Set functions. The GetObjectClass function (described later) can be used to get a jclass suitable for the first param- eter to this function. The name is the name of the field, and the sig parameter is the field descriptor. If this function fails, it returns NULL. 443 Chapter 9: Interacting with C/C++ Using Java Native Interface 14_777106 ch09.qxp 11/28/06 10:36 PM Page 443 The GetField function returns the value of a particular field specified by fieldID that belongs to the Java object obj: [NativeType] Get[Type]Field(jobject obj, jfieldID fieldID); The SetField function sets the value of a particular field specified by fieldID that belongs to the Java object obj to the value val: void Set[Type]Field(jobject obj, jfieldID fieldID, [NativeType] val); The GetStaticFieldID function works the same as GetFieldID but is used for getting a handle to a static field: jfieldID GetStaticFieldID(jclass clazz, const char *name, const char *sig); The GetStaticField function returns the value of a static field specified by the fieldID handle and belonging to the class described by clazz: [NativeType] GetStatic[Type]Field(jclass clazz, jfieldID fieldID); The SetStaticField function sets the value of a static field specified by the fieldID that belongs to the class described by clazz: void SetStatic[Type]Field(jclass clazz, jfieldID fieldID, [NativeType] value); Here’s an example of accessing fields on an object. The Java code defines a Point class and the native code performs some transformation on that point: class Point { public int x, y, z; public String toString() { return(“(“ + x + “, “ + y + “, “ + z + “)”); } } public class FieldAccessExample { public native void transformPoint(Point p); static { System.loadLibrary(“FieldAccessLibrary”); } public static void main(String args[]) { FieldAccessExample fae = new FieldAccessExample(); Point p1 = new Point(); p1.x = 17; p1.y = 20; p1.z = 10; System.out.println(“The point before transformation: “ + p1); 444 Part II: A Broad Understanding of Java APIs, Tools, and Techniques 14_777106 ch09.qxp 11/28/06 10:36 PM Page 444 fae.transformPoint(p1); System.out.println(“The point after transformation: “ + p1); } } The native library is loaded as usual. An instance of the Point class is created and set up, then the native function is called. The native code accesses the fields in the Point object and modifies these fields. Note that the object passed in isn’t a copy — any changes done to it in native code take effect in the Java code when the native function returns. The GetObjectClass function is used to get a handle to the class behind a specified object: JNIEXPORT void JNICALL Java_FieldAccessExample_transformPoint (JNIEnv *env, jobject obj, jobject thePoint) { jfieldID x_id, y_id, z_id; jint x_value, y_value, z_value; jclass cls; cls = env->GetObjectClass(thePoint); x_id = env->GetFieldID(cls, “x”, “I”); y_id = env->GetFieldID(cls, “y”, “I”); z_id = env->GetFieldID(cls, “z”, “I”); x_value = env->GetIntField(thePoint, x_id); y_value = env->GetIntField(thePoint, y_id); z_value = env->GetIntField(thePoint, z_id); x_value = x_value; y_value = 10*y_value + 5; z_value = 30*z_value + 2; env->SetIntField(thePoint, x_id, x_value); env->SetIntField(thePoint, y_id, y_value); env->SetIntField(thePoint, z_id, z_value); } In this case, GetObjectClass returns a handle to the Point class. Each field is an integer, so the field descriptor used is simply I. After the field IDs are retrieved, accessing the value of the field happens through GetIntField and the field values are written back using SetIntField. Invoking Java Methods Using JNI Just like fields, there are static and nonstatic methods in Java. JNI provides functions to execute methods on Java objects and also static methods on Java classes. Much like accessing fields, the name and a descriptor for the method are used in order to get a handle to a specific Java method. Once you have this handle, you pass it to one of the CallMethod functions along with the actual parameters for the method. There are actually a number of CallMethod functions — one for each possible return type from a method. Consult the previous table for a listing of the various return types. The method descriptor is formed by placing all the method’s parameter types inside a single set of parentheses, and then specifying the return type after the closing parenthesis. Types for parameters and return type use the field descriptor described in the previous section. If the method returns void, the 445 Chapter 9: Interacting with C/C++ Using Java Native Interface 14_777106 ch09.qxp 11/28/06 10:36 PM Page 445 descriptor is simply V. If the method does not take any parameters, the parentheses are left empty. The method descriptor for the main method that you are familiar with is ([Ljava/lang/String;)V. The parameters to main are placed inside the parentheses. A single open square bracket is used for each dimension of an object array, in this case a single one for a one-dimensional array of String, which is specified immediately after the bracket. Outside the parentheses is a single V because main has void as its return type. If you wish to invoke the constructor, use the method name <init>, and for static con- structors, use the name <clinit>. Following is a list of functions for use when invoking methods on Java objects. The various CallMethod functions have versions for each data type, much like the functions for accessing fields, so the abbrevia- tion is also used here. Replace the [NativeType] with a native data type, and replace the [Type] with the type name to finish the name of the function. The GetObjectClass function returns a jclass that represents the class of the Java object obj that is passed in: jclass GetObjectClass(jobject obj); The GetMethodID and GetStaticMethodID functions return a handle to the specified method for use in the various CallMethod functions: jmethodID GetMethodID(jclass clazz, const char *name, const char *sig); jmethodID GetStaticMethodID(jclass clazz, const char *name, const char *sig); A shortcut to deriving field and method descriptors can be found in the javap util- ity that comes with the JDK. By passing the command-line option -s to javap, you get a listing of the descriptors for the methods and fields of a class. For example, running javap on the Point class generates the following output: H:\CHAPTER9\code>javap -s Point Compiled from FieldAccessExample.java class Point extends java.lang.Object { public int x; /* I */ public int y; /* I */ public int z; /* I */ Point(); /* ()V */ public java.lang.String toString(); /* ()Ljava/lang/String; */ } Both field descriptors and method descriptors are output. You can copy these descriptors directly into the calls to the GetFieldID or GetMethodID functions instead of figuring the descriptors out manually. 446 Part II: A Broad Understanding of Java APIs, Tools, and Techniques 14_777106 ch09.qxp 11/28/06 10:36 PM Page 446 The GetObjectClass function can be used to get a jclass suitable for the first parameter to this func- tion. The name is the name of the method, and the sig parameter is the method descriptor. If this func- tion fails it returns NULL. The CallMethod functions (and variants) are used to invoke an instance method on a Java object: [NativeType] Call[Type]Method (jobject obj, jmethodID methodID, ); [NativeType] Call[Type]MethodV(jobject obj, jmethodID methodID, va_list args); [NativeType] Call[Type]MethodA(jobject obj, jmethodID methodID, const jvalue *args); The first two parameters to these functions are a handle to the object that has the method, and the han- dle to the specific method to invoke. The other parameters are the actual parameters to the Java method about to be invoked. The first function accepts a variable number of arguments and passes these argu- ments directly to the Java method. The second function accepts the list of arguments as a va_list struc- ture that is prepackaged with the list of arguments. The third function accepts the method arguments as an array of jvalue. The jvalue type is a union made up of all the native Java data types, including jobject. Thus, each instance of a jvalue represents a single Java native type. If you wish to invoke a constructor or a private method, the method ID has to be obtained based on the actual class of the object, not one of the object’s superclasses. The CallNonvirtual functions also invoke an instance method of an object, but which Java method to invoke is based on the clazz parameter: [NativeType] CallNonvirtual[Type]Method(jobject obj, jclass clazz, jmethodID methodID, ); [NativeType] CallNonvirtual[Type]MethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args); [NativeType] CallNonvirtual[Type]MethodA(jobject obj, jclass clazz, jmethodID methodID, const jvalue *args); These enable you to invoke a specific method somewhere in the hierarchy of the object’s class instead of invoking a method based on just the object’s class. Just like the normal CallMethod functions, these allow you to pass in arguments to the Java method in the same three different ways. The CallStaticMethod functions (and variants) invoke a static method belonging to the class clazz that is passed in: [NativeType] CallStatic[Type]Method(jclass clazz, jmethodID methodID, ); [NativeType] CallStatic[Type]MethodV(jclass clazz, jmethodID methodID, va_list args); [NativeType] CallStatic[Type]MethodA(jclass clazz, jmethodID methodID, const jvalue *args); Use GetStaticMethodID to obtain a handle to the specific method to invoke. Arguments to the method can be passed in to this function in the same three ways as described previously. 447 Chapter 9: Interacting with C/C++ Using Java Native Interface 14_777106 ch09.qxp 11/28/06 10:36 PM Page 447 [...]... jmethodID addFolderID = env->GetMethodID(cls, “addFolder”, “(Ljava/lang/String;)V”); This code establishes handles to the clearFolderList and addFolder methods defined in the Java code These handles are then used to invoke the methods on the Java side in order to communicate data back to the Java object 466 Chapter 9: Interacting with C/C++ Using Java Native Interface // First reset the list of folders... folderName for(l=1; l GetCount()); l++) { FolderPtr tempFolder = pPSTFolders->GetItem(l); 468 Chapter 9: Interacting with C/C++ Using Java Native Interface _bstr_t pstName = tempFolder->GetName(); _bstr_t compSearchName = searchName; if(pstName == compSearchName) { break; } } if(l > (long)pPSTFolders->GetCount()) { env->ThrowNew(env->FindClass( java/ lang/Exception”), “Could not... “Ljava/lang/String;Ljava/lang/String;)V”); mClearMessages = env->GetMethodID(mapiSupportClass, “clearMessageList”, 469 Part II: A Broad Understanding of Java APIs, Tools, and Techniques “(Ljava/lang/String;)V”); if(mAddMessage == NULL || mClearMessages == NULL) { printf(“Can’t obtain handle to class\n”); env->ThrowNew(env->FindClass( java/ lang/Exception”), “Can’t obtain handle to addMessage” “ or clearMessageList Java. .. folderName); } 470 Chapter 9: Interacting with C/C++ Using Java Native Interface The Java code and C++ code work together to create a miniature email client The Java code is responsible for the user interface and storing the message and folder information The C++ code is responsible for using COM to access the folders and email in MS Outlook Java Native Interface is the technology that allows Java code to... if(l > (long)pInfoStores->GetCount() || pTopFolder==NULL) { env->ThrowNew(env->FindClass( java/ lang/Exception”), “Can’t get pointer to top folder”); return; } pPSTFolders = pTopFolder->GetFolders(); if(pPSTFolders == NULL) { env->ThrowNew(env->FindClass( java/ lang/Exception”), “Can’t create global reference to Java class”); return; } The InfoStores collection contains all the top-level folders This... Because Java is a multithreaded environment, routines related to threading are available on the native side JNI also supports a way of exposing native routines to Java code singly, rather than making all native functions immediately available through a call to System.load or System.loadLibrary In addition to these features, Java exposes the reflection library natively Java Threading Because Java is... it, exit the loop break; } } if(pTopFolder == NULL || l==(long)pInfoStores->GetCount()) { env->ThrowNew(env->FindClass( java/ lang/Exception”), “Can’t obtain handle to top folder”); return; } pPSTFolders = pTopFolder->GetFolders(); if(pPSTFolders == NULL) { env->ThrowNew(env->FindClass( java/ lang/Exception”), “Can’t obtain handle to PST folders”); return; } This block of code will look familiar to you... determining factor for which method to execute is the method ID that is passed in Handling Java Exceptions in Native Code JNI provides hooks to the Java exception mechanism in order to handle exceptions that are thrown in the course of executing methods that are implemented in Java code, or native methods written to throw Java exceptions This mechanism has no bearing on standard error handling for regular... env->GetObjectClass(obj); if(mapiSupportClass == NULL) { env->ThrowNew(env->FindClass( java/ lang/Exception”), “Can’t obtain class handle from object passed in”); return; } _SessionPtr pSession(“MAPI.Session”); // Log on with a specific profile // If not specified a logon box would pop up pSession->Logon(profile); 4 67 Part II: A Broad Understanding of Java APIs, Tools, and Techniques The three jstrings that are passed... setSort(int whichSort); static { System.loadLibrary(“RegisterNativeLibrary”); } public static void main(String args[]) 4 57 Part II: A Broad Understanding of Java APIs, Tools, and Techniques { RegisterNativeExample rne = new RegisterNativeExample(); int sortType = 1; int nums[] = {23, 1, 6, 1, 2, 7, 3, 4}; try { BufferedReader br = new BufferedReader( new InputStreamReader(System.in)); System.out.println(“Choose . type java. lang.Integer is described by Ljava/lang/Integer;. 442 Part II: A Broad Understanding of Java APIs, Tools, and Techniques 14 _77 71 06 ch09.qxp 11/28/ 06 10: 36 PM Page 442 Primitive Type (Java) . described previously. 4 47 Chapter 9: Interacting with C/C++ Using Java Native Interface 14 _77 71 06 ch09.qxp 11/28/ 06 10: 36 PM Page 4 47 Along with showing how to invoke Java methods, the following. figuring the descriptors out manually. 4 46 Part II: A Broad Understanding of Java APIs, Tools, and Techniques 14 _77 71 06 ch09.qxp 11/28/ 06 10: 36 PM Page 4 46 The GetObjectClass function can be used