|
Of course, Java programming language functions can call C functionsthat is what native methods are for. Can we go the other way? Why would we want to do this anyway? The answer is that it often happens that a native method needs to request a service from an object that was passed to it. We first show you how to do it for non-static methods, and then we show you how to do it for static methods. Non-Static MethodsAs an example of calling a Java method from native code, let's enhance the Printf class and add a method that works similarly to the C function fprintf. That is, it should be able to print a string on an arbitrary PrintWriter object. class Printf3 { public native static void fprint(PrintWriter out, String s, double x); . . . } We first assemble the string to be printed into a String object str, as in the sprint method that we already implemented. Then, we call the print method of the PrintWriter class from the C function that implements the native method. You can call any Java method from C by using the function call
Replace Xxx with Void, Int, Object, etc., depending on the return type of the method. Just as you need a fieldID to access a field of an object, you need a method ID to call a method. You obtain a method ID by calling the JNI function GetMethodID and supplying the class, the name of the method, and the method signature. In our example, we want to obtain the ID of the print method of the PrintWriter class. As you saw in Volume 1, Chapter 12, the PrintWriter class has several overloaded methods called print. For that reason, you must also supply a string describing the parameters and return the value of the specific function that you want to use. For example, we want to use void print(java.lang.String). As described in the preceding section, we must now "mangle" the signature into the string "(Ljava/lang/String;)V". Here is the complete code to make the method call, by
Examples 11-14 and 11-15 show the Java code for a test program and the Printf3 class. Example 11-16 contains the C code for the native fprint method. NOTE
Static MethodsCalling static methods from native methods is similar to calling non-static methods. There are two differences.
As an example of this, let's make the call to the static method System.getProperty("java.class.path") from a native method. The return value of this call is a string that gives the current class path. First, we have to find the class to use. Because we have no object of the class System readily available, we use FindClass rather than GetObjectClass. jclass class_System = (*env)->FindClass(env, "java/lang/System"); Next, we need the ID of the static getProperty method. The encoded signature of that method is "(Ljava/lang/String;)Ljava/lang/String;" since both the parameter and the return value are a string. Hence, we obtain the method ID as follows: jmethodID id_getProperty = (*env)->GetStaticMethodID(env, class_System, "getProperty", "(Ljava/lang/String;)Ljava/lang/String;"); Finally, we can make the call. Note that the class object is passed to the CallStaticObjectMethod function. jobject obj_ret = (*env)->CallStaticObjectMethod(env, class_System, id_getProperty, (*env)->NewStringUTF(env, "java.class.path")); The return value of this method is of type jobject. If we want to manipulate it as a string, we must cast it to jstring: jstring str_ret = (jstring) obj_ret; C++ NOTE
ConstructorsA native method can create a new Java object by invoking its constructor. You invoke the constructor by calling the NewObject function.
You obtain the method ID needed for this call from the GetMethodID function by specifying the method name as "<init>" and the encoded signature of the constructor (with return type void). For example, here is how a native method can create a FileOutputStream object. const char[] fileName = ". . ."; jstring str_fileName = (*env)->NewStringUTF(env, fileName); jclass class_FileOutputStream = (*env)->FindClass(env, "java/io/FileOutputStream"); jmethodID id_FileOutputStream = (*env)->GetMethodID(env, class_FileOutputStream, "<init>", "(Ljava/lang/String;)V"); jobject obj_stream = (*env)->NewObject(env, class_FileOutputStream, id_FileOutputStream, str_fileName); Note that the signature of the constructor takes a parameter of type java.lang.String and has a return type of void. Alternative Method InvocationsSeveral variants of the JNI functions call a Java method from native code. These are not as important as the functions that we already discussed, but they are occasionally useful. The CallNonvirtualXxxMethod functions receive an implicit argument, a method ID, a class object (which must correspond to a superclass of the implicit argument), and explicit arguments. The function calls the version of the method in the specified class, bypassing the normal dynamic dispatch mechanism. All call functions have versions with suffixes "A" and "V" that receive the explicit parameters in an array or a va_list (as defined in the C header stdarg.h). Example 11-14. Printf3Test.java1. import java.io.*; 2. 3. class Printf3Test 4. { 5. public static void main(String[] args) 6. { 7. double price = 44.95; 8. double tax = 7.75; 9. double amountDue = price * (1 + tax / 100); 10. PrintWriter out = new PrintWriter(System.out); 11. Printf3.fprint(out, "Amount due = %8.2f\n", amountDue); 12. out.flush(); 13. } 14. } Example 11-15. Printf3.java1. import java.io.*; 2. 3. class Printf3 4. { 5. public static native void fprint(PrintWriter out, String format, double x); 6. 7. static 8. { 9. System.loadLibrary("Printf3"); 10. } 11. } Example 11-16. Printf3.c[View full width] 1. #include "Printf3.h" 2. #include <string.h> 3. #include <stdlib.h> 4. #include <float.h> 5. 6. /** 7. @param format a string containing a printf format specifier 8. (such as "%8.2f"). Substrings "%%" are skipped. 9. @return a pointer to the format specifier (skipping the '%') 10. or NULL if there wasn't a unique format specifier 11. */ 12. char* find_format(const char format[]) 13. { 14. char* p; 15. char* q; 16. 17. p = strchr(format, '%'); 18. while (p != NULL && *(p + 1) == '%') /* skip %% */ 19. p = strchr(p + 2, '%'); 20. if (p == NULL) return NULL; 21. /* now check that % is unique */ 22. p++; 23. q = strchr(p, '%'); 24. while (q != NULL && *(q + 1) == '%') /* skip %% */ 25. q = strchr(q + 2, '%'); 26. if (q != NULL) return NULL; /* % not unique */ 27. q = p + strspn(p, " -0+#"); /* skip past flags */ 28. q += strspn(q, "0123456789"); /* skip past field width */ 29. if (*q == '.') { q++; q += strspn(q, "0123456789"); } 30. /* skip past precision */ 31. if (strchr("eEfFgG", *q) == NULL) return NULL; 32. /* not a floating point format */ 33. return p; 34. } 35. 36. JNIEXPORT void JNICALL Java_Printf3_fprint(JNIEnv* env, jclass cl, 37. jobject out, jstring format, jdouble x) 38. { 39. const char* cformat; 40. char* fmt; 41. jstring str; 42. jclass class_PrintWriter; 43. jmethodID id_print; 44. 45. cformat = (*env)->GetStringUTFChars(env, format, NULL); 46. fmt = find_format(cformat); 47. if (fmt == NULL) 48. str = format; 49. else 50. { 51. char* cstr; 52. int width = atoi(fmt); 53. if (width == 0) width = DBL_DIG + 10; 54. cstr = (char*) malloc(strlen(cformat) + width); 55. sprintf(cstr, cformat, x); 56. str = (*env)->NewStringUTF(env, cstr); 57. free(cstr); 58. } 59. (*env)->ReleaseStringUTFChars(env, format, cformat); 60. 61. /* now call ps.print(str) */ 62. 63. /* get the class */ 64. class_PrintWriter = (*env)->GetObjectClass(env, out); 65. 66. /* get the method ID */ 67. id_print = (*env)->GetMethodID(env, class_PrintWriter, "print", "(Ljava/lang /String;)V"); 68. 69. /* call the method */ 70. (*env)->CallVoidMethod(env, out, id_print, str); 71. } Executing Java Methods from C Code
|
|