|
Suppose you have a C function that does something you like and, for one reason or another, you don't want to bother reimplementing it in the Java programming language. For the sake of illustration, we assume it is the useful and venerable printf function. You want to be able to call printf from your programs. The Java programming language uses the keyword native for a native method, and you will obviously need to encapsulate the printf function in a class. So, you might write something like this: public class Printf { public native String printf(String s); } You actually can compile this class, but when you go to use it in a program, then the virtual machine will tell you it doesn't know how to find printfreporting an UnsatisfiedLinkError. So the trick is to give the run time enough information so that it can link in this class. As you will soon see, under the JDK this requires a three-step process:
We now show you how to carry out these steps for various kinds of examples, starting from a trivial special-case use of printf and ending with a realistic example involving the registry functions for Windowsplatform-dependent functions that are obviously not available directly from the Java platform. Working with the printf FunctionLet's start with just about the simplest possible situation using printf: calling a native method that prints the message, "Hello, Native World." Obviously we are not even tapping into the useful formatting features of printf! Still, this is a good way for you to test that your C compiler works as expected before you try implementing more ambitious native methods. You first declare the native method in a class. The native keyword alerts the compiler that the method will be defined externally. Of course, native methods will contain no code in the Java programming language, and the method header is followed immediately by a terminating semicolon. This means, as you saw in the example above, native method declarations look similar to abstract method declarations. class HelloNative { public native static void greeting(); . . . } In this particular example, note that the native method is also declared as static. Native methods can be both static and non-static. We start with a static method because we do not yet want to deal with parameter passing. Next, write a corresponding C function. You must name that function exactly the way the Java runtime environment expects. Here are the rules:
NOTE
Actually, nobody does this by hand; instead, you run the javah utility, which automatically generates the function names. To use javah, first, compile the source file (given in Example 11-3 on page 834). javac HelloNative.java Next, call the javah utility to produce a C header file. The javah executable can be found in the jdk/bin directory. javah HelloNative Using javah creates a header file, HelloNative.h, as in Example 11-1. Example 11-1. HelloNative.h1. /* DO NOT EDIT THIS FILE - it is machine generated */ 2. #include <jni.h> 3. /* Header for class HelloNative */ 4. 5. #ifndef _Included_HelloNative 6. #define _Included_HelloNative 7. #ifdef __cplusplus 8. extern "C" { 9. #endif 10. /* 11. * Class: HelloNative 12. * Method: greeting 13. * Signature: ()V 14. */ 15. JNIEXPORT void JNICALL Java_HelloNative_greeting(JNIEnv *, jclass); 16. 17. ifdef __cplusplus 18. } 19. #endif 20. #endif As you can see, this file contains the declaration of a function Java_HelloNative_greeting. (The strings JNIEXPORT and JNICALL are defined in the header file jni.h. They denote compiler-dependent specifiers for exported functions that come from a dynamically loaded library.) Now, you simply have to copy the function prototype from the header file into the source file and give the implementation code for the function, as shown in Example 11-2. Example 11-2. HelloNative.c1. #include "HelloNative.h" 2. #include <stdio.h> 3. 4. JNIEXPORT void JNICALL Java_HelloNative_greeting(JNIEnv* env, jclass cl) 5. { 6. printf("Hello Native World!\n"); 7. } In this simple function, ignore the env and cl arguments. You'll see their use later. C++ NOTE
You compile the native C code into a dynamically loaded library. The details depend on your compiler. For example, with the Gnu C compiler on Linux, use these commands:
With the Sun compiler under the Solaris Operating System, the command is
With the Microsoft C++ compiler under Windows, the command is
Here, jdk is the directory that contains the JDK. TIP
You can also use the freely available Cygwin programming environment, available from http://www.cygwin.com. It contains the Gnu C compiler and libraries for UNIX-style programming on Windows. With Cygwin, use the command
Type the entire command on a single line. NOTE
Finally, add a call to the System.loadLibrary method in the Java class that defines the native method. This ensures that the virtual machine will load the library before the first use of the class. The easiest way to do this is with a static initialization block, as in Example 11-3. Example 11-3. HelloNative.java1. class HelloNative 2. { 3. public static native void greeting(); 4. 5. static 6. { 7. System.loadLibrary("HelloNative"); 8. } 9. } Assuming you have followed all the steps given above, you are now ready to run the HelloNativeTest application shown in Example 11-4. Example 11-4. HelloNativeTest.java1. class HelloNativeTest 2. { 3. public static void main(String[] args) 4. { 5. HelloNative.greeting(); 6. } 7. } If you compile and run this program, the message "Hello, Native World!" is displayed in a terminal window. NOTE
Of course, this is not particularly impressive by itself. However, if you keep in mind that this message is generated by the C printf command and not by any Java programming language code, you will see that we have taken the first steps toward bridging the gap between the two languages! java.lang.System 1.0
NOTE
|
|