Академический Документы
Профессиональный Документы
Культура Документы
108/1/
Let's start with just about the simplest possible situation using printf:
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:
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
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.
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
enough information so that it can link in this class. As you will soon see,
1.
Generate a C stub for a function that translates between the Java call
and the actual C function. The stub does this translation by taking
2.
Create a special shared library and export the stub from it.
3.
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.
class HelloNative
{
public native static void greeting();
. . .
}
In this particular example, note that the native method is also declared as
static.
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:
Use the full Java method name, such as HelloNative.greeting. If the class is in
a package, then prepend the package name, such as
com.horstmann.HelloNative.greeting.
2. Replace every period with an underscore, and append the prefix Java_. For
example, Java_HelloNative_greeting or
Java_com_horstmann_HelloNative_greeting.
3. If the class name contains characters that are not ASCII letters or digitsthat
is, '_', '$', or Unicode characters with code greater than '\u007F'replace
them with _0xxxx, where xxxx is the sequence of four hexadecimal digits of
the character's Unicode value.
NOTE
Java_HelloNative_greeting__I.
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
Java_HelloNative_greeting.
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.c
1. #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
must then declare the functions that implement the native methods
as extern "C". (This stops the C++ compiler generating C++specific code.) For example,
#include "HelloNative.h"
#include <stdio.h>
extern "C"
JNIEXPORT void JNICALL Java_HelloNative_greeting(JNIEnv* env,
jclass cl)
{
printf("Hello, Native World!\n");
}
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:
gcc -fPIC -I
With the Sun compiler under the Solaris Operating System, the command is
cc -G -I
cl -I
monstrosity).
(or a similar
You can also use the freely available Cygwin programming environment,
jdk/include/win32
jdk/include/ -
The Windows version of the header file jni_md.h contains the type
declaration
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.java
3.
7.
1. class HelloNative
2. {
public static native void greeting();
4.
5.
static
6.
{
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.java
3.
1. class HelloNativeTest
2. {
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
If you run Linux, you must add the current directory to the library
path. Either set the LD_LIBRARY_PATH environment variable,
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
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
loads the library with the given name. The library is located in the library
search path. The exact method for locating the library is operating-system
dependent. Under Windows, this method searches first the current directory,
then the directories listed in the PATH environment variable.
loads the library with the given file name. If the library is not found, then an
UnsatisfiedLinkError
NOTE
is thrown.
Some shared libraries for native code must run initialization code.
You can place any initialization code into a JNI_OnLoad method.
Similarly, when the VM shuts down, it will call the JNI_OnUnload
method if you provide it. The prototypes are
others they are 32-bit quantities. In the Java platform, of course, an int is
always a 32-bit integer. For that reason, the Java Native Interface defines
types jint, jlong, and so on.
Table 11-1 shows the correspondence between Java types and C types.
Table 11-1. Java Types and C Types
C Programming Language
Bytes
byte
jbyte
char
jchar
short
jshort
int
jint
long
jlong
float
jfloat
double
jdouble
boolean
jboolean
1
2
2
4
8
4
8
In the header file jni.h, these types are declared with typedef statements as
the equivalent types on the target platform. That header file also defines the
constants JNI_FALSE = 0 and JNI_TRUE = 1.
Until JDK 5.0, Java had no direct analog to the C printf function. Let's
suppose you are stuck with an older JDK release and decide to implement
Example 11-5 shows a class called Printf1 that uses a native method to print
When passing numbers between C and the Java programming language, you
should understand which types correspond to each other. For example,
although C does have data types called int and long, their implementation is
platform dependent. On some platforms, ints are 16-bit quantities, and on
3.
1. class Printf1
2. {
public static native int print(int width, int precision, double x);
4.
5.
static
6.
{
7.
System.loadLibrary("Printf1");
8.
}
9. }
Notice that when the method is implemented in C, all int and double
converts them to arrays of UTF-16 values, that is, to jchar arrays. (The UTF8, "modified UTF-8", and UTF-16 formats were discussed in Volume 1,
Chapter 12. Recall that the "modified UTF-8" encoding leaves ASCII
The function simply assembles a format string "%w.pf" in the variable fmt,
Example 11-7 shows the test program that demonstrates the Printf1 class.
Example 11-7. Printf1Test.java
1. class Printf1Test
2. {
3.
public static void main(String[] args)
4.
{
5.
int count = Printf1.print(8, 4, 3.14);
6.
count += Printf1.print(8, 4, count);
7.
System.out.println();
8.
for (int i = 0; i < count; i++)
9.
System.out.print("-");
10.
System.out.println();
11.
}
12. }
If your C code already uses Unicode, you'll want to use the second set of
conversion functions. On the other hand, if all your strings are restricted to
ASCII characters, you can use the "modified UTF-8" conversion functions.
A native method with a String parameter actually receives a value of an
opaque type called jstring. A native method with a return value of type
String
must return a value of type jstring. JNI functions read and construct
these jstring objects. For example, the NewStringUTF function makes a new
jstring
object out of a char array that contains ASCII characters or, more
generally, "modified UTF-8"-encoded byte sequences.
JNI functions have a somewhat odd calling convention. Here is a call to the
NewStringUTF
function.
Java Native Interface has two sets of functions for manipulating strings, one
All calls to JNI functions use the env pointer that is the first argument of
String Parameters
sequences of bytes, so strings are quite different in the two languages. The
that converts Java strings to "modified UTF-8" byte sequences and one that
pointers (see Figure 11-1). Therefore, you must prefix every JNI call with
(*env)->
memory block that will be filled with the "modified UTF-8" equivalents.
The virtual machine must know when you are finished using the string so
Alternatively, you can supply your own buffer to hold the string characters by
calling the GetStringRegion or GetStringUTFRegion methods.
http://java.sun.com/j2se/5.0/docs/guide/jni/spec/functions.html.
C++ NOTE
function pointer lookup for you. For example, you can call the
NewStringUTF
returns a new Java string object from a "modified UTF-8" byte sequence, or
function as
jstr = env->NewStringUTF(greeting);
Note that you omit the JNIEnv pointer from the parameter list of the
Parameters:
call.
The NewStringUTF function lets you construct a new jstring. To read the
characters that describe the character string. Note that a specific virtual
machine is free to choose this character encoding for its internal string
representation, so you may get a character pointer into the actual Java string.
the other hand, if the virtual machine uses UTF-16 or UTF-32 characters for
its internal string representation, then this function call allocates a new
bytes
returns the number of bytes required for the "modified UTF-8" encoding.
Parameters:
Because Java strings are meant to be immutable, it is very important that you
treat the const seriously and do not try to write into this character array. On
NULL
env
env
string
Parameters:
ReleaseStringUTFChars
env
string
isCopy
is called.
supplied buffer. The buffer must be long enough to hold the bytes. In the
returns a new Java string object from a Unicode string, or NULL if the string
Parameters:
cannot be constructed.
env
chars
length
informs the virtual machine that the native code no longer needs access to
Parameters:
env
string
bytes
Parameters:
string
Parameters:
buffer.
Parameters:
env
string
isCopy
called.
informs the virtual machine that the native code no longer needs access to
the Java string through chars.
Parameters:
env
string
chars
Let us put these functions we just described to work and write a class that
calls the C function sprintf. We would like to call the function as shown in
Example 11-8.
Example 11-8. Printf2Test.java
9.
1. class Printf2Test
2. {
3.
public static void main(String[] args)
4.
{
5.
double price = 44.95;
6.
double tax = 7.75;
7.
double amountDue = price * (1 + tax / 100);
8.
String s = Printf2.sprint("Amount due = %8.2f", amountDue);
10.
System.out.println(s);
11.
}
12. }
Example 11-9 shows the class with the native sprint method.
Example 11-9. Printf2.java
3.
1. class Printf2
2. {
public static native String sprint(String format, double x);
4.
5.
static
6.
{
7.
System.loadLibrary("Printf2");
8.
}
9. }
Example 11-10 shows the code for the C implementation. Note the calls to
GetStringUTFChars
1. #include "Printf2.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 jstring JNICALL Java_Printf2_sprint(JNIEnv* env, jclass cl,
37.
jstring format, jdouble x)
38. {
39.
const char* cformat;
40.
char* fmt;
41.
jstring ret;
42.
43.
cformat = (*env)->GetStringUTFChars(env, format, NULL);
44.
fmt = find_format(cformat);
45.
if (fmt == NULL)
46.
ret = format;
47.
else
48.
{
49.
char* cret;
50.
int width = atoi(fmt);
51.
if (width == 0) width = DBL_DIG + 10;
52.
57.
In this function, we chose to keep the error handling simple. If the format
code to print a floating-point number is not of the form %w.pc, where c is
Suppose you have a C function that does something you like and, for one reason or another,
you dont want to bother reimplementing it in Java. For the sake of illustration, well start
with a simple C function that prints a greeting.
The Java programming language uses the keyword native for a native method, and you
will obviously need to place a method in a class. The result is shown in Listing 12.1.
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. Therefore, native
method declarations look similar to abstract ...
Accessing Fields
All the native methods that you saw so far were static methods with number
and string parameters. We next consider native methods that operate on
In order to see how to access instance fields from a native method, we will
Note the second argument. It is no longer of type jclass but of type jobject.
In fact, it is the equivalent of the this reference. Static methods obtain a
Now we access the salary field of the implicit argument. In the "raw" Java-toC binding of Java 1.0, this was easya programmer could directly access
object data fields. However, direct access requires all virtual machines to
expose their internal data layout. For that reason, the JNI requires
programmers to get and set the values of data fields by calling special JNI
functions.
Here, class is a value that represents a Java object of type Class, fieldID is a
value of a special type, jfieldID, that identifies a field in a structure, and Xxx
represents a Java data type (Object, Boolean, Byte, and so on). There are two
ways to obtain the class object. The GetObjectClass function returns the class
of any object. For example:
The FindClass function lets you specify the class name as a string (curiously,
with / instead of periods as package name separators).
Class references are only valid until the native method returns.
Use the GetFieldID function to obtain the fieldID. You must supply the name
method call. You must call GetObjectClass every time the native
of the field and its signature, an encoding of its type. For example, here is
The string "D" denotes the type double. You learn the complete rules for
encoding signatures in the next section.
You may be thinking that accessing a data field seems quite convoluted. The
designers of the JNI did not want to expose the data fields directly, so they
had to supply functions for getting and setting field values. To minimize the
cost of these functions, computing the field ID from the field namewhich is
Now you can use the class reference and field IDs in subsequent
the most expensive stepis factored out into a separate step. That is, if you
calls. When you are done using the class, make sure to call
repeatedly get and set the value of a particular field, you incur only once the
(*env)->DeleteGlobalRef(env, class_X);
Let us put all the pieces together. The following code reimplements the
raiseSalary
CAUTION
Examples 11-11 and 11-12 show the Java code for a test program and the
Employee
class. Example 11-13 contains the C code for the native raiseSalary
method.
Example 11-11. EmployeeTest.java
3.
5.
6.
name = n;
salary = s;
7.
}
8.
9.
public native void raiseSalary(double byPercent);
10.
11.
public void print()
12.
{
13.
System.out.println(name + " " + salary);
14.
}
15.
16.
private String name;
17.
private double salary;
18.
19.
static
20.
{
21.
System.loadLibrary("Employee");
22.
}
23. }
1. #include "Employee.h"
2.
3. #include <stdio.h>
4.
5. JNIEXPORT void JNICALL Java_Employee_raiseSalary(JNIEnv* env, jobject
this_obj,
jdouble byPercent)
6. {
7.
/* get the class */
8.
jclass class_Employee = (*env)->GetObjectClass(env, this_obj);
9.
10.
/* get the field ID */
11.
jfieldID id_salary = (*env)->GetFieldID(env, class_Employee,
"salary", "D");
12.
13.
/* get the field value */
14.
jdouble salary = (*env)->GetDoubleField(env, this_obj, id_salary);
15.
16.
salary *= 1 + byPercent / 100;
17.
18.
/* set the field value */
19.
(*env)->SetDoubleField(env, this_obj, id_salary, salary);
20. }
Parameters:
returns the value of a field. The field type Xxx is one of Object, Boolean, Byte,
Parameters:
obj
id
or Double.
env
Xxx value)
sets a field to a new value. The field type Xxx is one of Object, Boolean, Byte,
Parameters:
env
obj
id
value
or Double.
Parameters:
cl
name
sig
returns the value of a static field. The field type Xxx is one of Object, Boolean,
Parameters:
env
Xxx value)
sets a static field to a new value. The field type Xxx is one of Object, Boolean,
Byte, Char, Short, Int, Long, Float,
env
or Double.
cl
id
value
Accessing static fields is similar to accessing non-static fields. You use the
GetStaticFieldID
To access instance fields and call methods that are defined in the Java
programming language, you need to learn the rules for "mangling" the
Parameters:
Encoding Signatures
cl
id
or Double.
You supply the class, not the instance object, when accessing the field.
scheme:
byte
char
double
float
I
J
int
Lclassname;
S
V
Z
long
a class type
short
void
boolean
Note that the semicolon at the end of the L expression is the terminator of
the type expression, not a separator between parameters. For example, the
constructor
has a signature
"(Ljava/lang/String;DLjava/util/Date;)V"
[[F
For the complete signature of a method, you list the parameter types inside a
pair of parentheses and then list the return type. For example, a method
receiving two integers and returning an integer is encoded as
(II)I
The print method that we used in the preceding example has a mangled
signature of
(Ljava/lang/String;)V
You can use the javap command with option -s to generate the
method signatures from class files. For example, run
javap -s
Encoding Signatures
To access instance fields and call methods that are defined in the Java
programming language, you need to learn the rules for "mangling" the
double
float
int
Lclassname;
S
char
Also note that in this encoding scheme, you must use / instead of . to
[Ljava/lang/String;
byte
C
F
scheme:
V
Z
long
a class type
short
void
boolean
Note that the semicolon at the end of the L expression is the terminator of
the type expression, not a separator between parameters. For example, the
constructor
has a signature
"(Ljava/lang/String;DLjava/util/Date;)V"
Also note that in this encoding scheme, you must use / instead of . to
separate the package and class names.
For the complete signature of a method, you list the parameter types inside a
pair of parentheses and then list the return type. For example, a method
receiving two integers and returning an integer is encoded as
(II)I
The print method that we used in the preceding example has a mangled
signature of
(Ljava/lang/String;)V
You can use the javap command with option -s to generate the
method signatures from class files. For example, run
javap -s -private Employee
You get the following output, displaying the signatures of all fields
and methods.
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 Methods
As an example of calling a Java method from native code, let's enhance the
Printf
fprintf.
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 of the PrintWriter class from the C function that implements the
native method.
NOTE
You can call any Java method from C by using the function call
>CallXxxMethod(env,
(*env)-
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
JNI function GetMethodID and supplying the class, the name of the method,
and the method signature.
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).
Static Methods
You supply a class object, not an implicit parameter object, when invoking
the method.
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
"(Ljava/lang/String;)Ljava/lang/String;"
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
The numerical method IDs and field IDs are conceptually similar
to Method and Field objects in the reflection API. You can convert
between them with the following functions:
since both the parameter and the return value are a string. Hence, we
obtain the method ID as follows:
Finally, we can make the call. Note that the class object is passed to the
CallStaticObjectMethod
function.
These are not as important as the functions that we already discussed, but
C++ NOTE
In C, the types jstring and jclass, as well as the array types that
are introduced later, are all type equivalent to jobject. The cast
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.java
1. 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. }
cast.
Constructors
A native method can create a new Java object by invoking its constructor.
You invoke the constructor by calling the NewObject function.
>NewObject(env,
You obtain the method ID needed for this call from the GetMethodID
signature of the constructor (with return type void). For example, here is
how a native method can create a FileOutputStream object.
Several variants of the JNI functions call a Java method from native code.
5.
1. import java.io.*;
2.
3. class Printf3
4. {
public static native void fprint(PrintWriter out, String format,
double x);
6.
7.
static
8.
{
9.
System.loadLibrary("Printf3");
10.
}
11. }
1. #include "Printf3.h"
2. #include <string.h>
3. #include <stdlib.h>
4. #include <float.h>
5.
6. /**
@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.
7.
67.
66.
/* get the method ID */
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. }
Parameters:
env
cl
name
sig
jfloat f;
jdouble d;
jobject l;
} jvalue;
Parameters:
env
obj
id
args
parameters after the method ID. The second function receives the method
Parameters:
env
cl
args
va_list,
id
call a method, bypassing dynamic dispatch. The return type Xxx is one of
args
args[])
id
cl
call a static method. The return type Xxx is one of Object, Boolean, Byte,
obj
args)
Parameters:
env
cl
sig
env
name
method name of "<init>" and a return type of void. The first function has a
variable number of argumentssimply append the method parameters after
the method ID. The second function receives the method arguments in an
array of jvalue. The third function receives the method parameters in a
Parameters:
va_list,
env
cl
id
args
Java Type
boolean[]
byte[]
char[]
int[]
short[]
long[]
float[]
double[]
Object[]
jbooleanArray
jbyteArray
jcharArray
jintArray
jshortArray
jlongArray
jfloatArray
jdoubleArray
jobjectArray
How you access elements in the array depends on whether the array stores
objects or a primitive type (bool, char, or a numeric type). You access
elements in an object array with the GetObjectArrayElement and
SetObjectArrayElement
methods.
jobjectArray array = . . .;
int i, j;
jobject x = (*env)->GetObjectArrayElement(env, array, i);
(*env)->SetObjectArrayElement(env, array, j, x);
While simple, this approach is also clearly inefficient; you want to be able to
access array elements directly, especially when doing vector and matrix
computations.
primitive type, that is, not Object. You can then read and write the array
elements directly. However, since the pointer may point to a copy, any
changes that you make are guaranteed to be reflected in the original array
only when you call the corresponding ReleaseXxxArrayElements function!
NOTE
jboolean
Here is a code sample that multiplies all elements in an array of double values
by a constant. We obtain a C pointer a into the Java array and then access
individual elements as a[i].
jdoubleArray array_a = . . .;
double scaleFactor = . . .;
double* a = (*env)->GetDoubleArrayElements(env, array_a, NULL);
for (i = 0; i < (*env)->GetArrayLength(env, array_a); i++)
a[i] = a[i] * scaleFactor;
(*env)->ReleaseDoubleArrayElements(env, array_a, a, 0);
Whether the virtual machine actually copies the array depends on how it
allocates arrays and does its garbage collection. Some "copying" garbage
collectors routinely move objects around and update object references. That
strategy is not compatible with "pinning" an array to a particular location,
because the collector cannot update the pointer values in native code.
NOTE
Parameters:
array
To access just a few elements of a large array, use the GetXxxArrayRegion and
SetXxxArrayRegion
You can create new Java arrays in native methods with the NewXxxArray
Parameters:
the array elements, and an initial element for all entries (typically, NULL). Here
is an example.
array
index
Arrays of primitive types are simpler. You just supply the length of the array.
Parameters:
NOTE
array
index
function. To create a new array of objects, you specify the length, the type of
value
yields a C pointer to the elements of a Java array. The field type Xxx is one of
Boolean, Byte, Char, Short, Int, Long, Float,
array
start
Parameters:
env
array
elems
isCopy
JNI_TRUE
no longer needed.
env
array
elems
mode
Parameters:
array elements
Xxx elems[])
copies elements from a Java array to a C array. The field type Xxx is one of
env
array
elems
or Double.
Handling Errors
env
length
needed
JNI_ABORT
start
copies elements from a C array to a Java array. The field type Xxx is one of
Xxx elems[])
JNI_COMMIT
Parameters:
length
or Double.
should report this problem to the Java virtual machine. Then, you would
exception object. When the native method exits, the Java virtual machine
throws that exception.
Throwable.
"<init>", "()V");
/* ID of default constructor */
jthrowable obj_exc = (*env)->NewObject(env, class_EOFException,
id_EOFException);
(*env)->Throw(env, obj_exc);
Both Throw and ThrowNew merely post the exception; they do not interrupt the
control flow of the native method. Only when the method returns does the
Java virtual machine throw the exception. Therefore, every call to THRow and
THRowNew
Normally, native code need not be concerned with catching Java exceptions.
However, when a native method calls a Java method, that method might
throw an exception. Moreover, a number of the JNI functions throw
exceptions as well. For example, SetObjectArrayElement throws an
ArrayIndexOutOfBoundsException
ArrayStoreException
current exception object. If you just want to check whether an exception has
been thrown, without obtaining a reference to the exception object, use
jboolean occurred = (*env)->ExceptionCheck(env);
In our next example, we implement the fprint native method with the
paranoia that is appropriate for a native method. Here are the exceptions
method from a native method, we send the string to the stream, a character
at a time, and call ExceptionOccurred after each call. Example 11-17 shows
the code for the native method, and Example 11-18 contains the definition
of the class containing the native method. Notice that the native method
does not immediately terminate when an exception occurs in the call to
PrintWriter.printit
returns, the virtual machine again raises the exception. The test program in
Example 11-19 demonstrates how the native method throws an exception
when the formatting string is not valid.
Example 11-17. Printf4.c
element class of the array. In situations like these, a native method should
call the ExceptionOccurred method to determine whether an exception has
been thrown. The call
that we throw:
7.
1. #include "Printf4.h"
2. #include <string.h>
3. #include <stdlib.h>
4. #include <float.h>
5.
6. /**
@param format a string containing a printf format specifier
8.
(such as "%8.2f"). Substrings "%%" are skipped.
9.
70.
5.
1. import java.io.*;
2.
3. class Printf4
4. {
public static native void fprint(PrintWriter ps, String format,
double x);
6.
7.
static
8.
{
9.
System.loadLibrary("Printf4");
10.
}
11. }
9.
10.
11.
1. import java.io.*;
2.
3. class Printf4Test
4. {
5.
public static void main(String[] args)
6.
{
7.
double price = 44.95;
8.
double tax = 7.75;
double amountDue = price * (1 + tax / 100);
PrintWriter out = new PrintWriter(System.out);
/* This call will throw an exception--note the %% */
12.
Parameters:
env
Parameters:
env
cl
msg
Parameters:
env
Handling Errors
should report this problem to the Java virtual machine. Then, you would
exception object. When the native method exits, the Java virtual machine
throws that exception.
Throwable.
Both Throw and ThrowNew merely post the exception; they do not interrupt the
control flow of the native method. Only when the method returns does the
Java virtual machine throw the exception. Therefore, every call to THRow and
THRowNew
Normally, native code need not be concerned with catching Java exceptions.
However, when a native method calls a Java method, that method might
throw an exception. Moreover, a number of the JNI functions throw
exceptions as well. For example, SetObjectArrayElement throws an
ArrayIndexOutOfBoundsException
ArrayStoreException
element class of the array. In situations like these, a native method should
call the ExceptionOccurred method to determine whether an exception has
been thrown. The call
current exception object. If you just want to check whether an exception has
been thrown, without obtaining a reference to the exception object, use
jboolean occurred = (*env)->ExceptionCheck(env);
In our next example, we implement the fprint native method with the
paranoia that is appropriate for a native method. Here are the exceptions
that we throw:
method from a native method, we send the string to the stream, a character
at a time, and call ExceptionOccurred after each call. Example 11-17 shows
the code for the native method, and Example 11-18 contains the definition
of the class containing the native method. Notice that the native method
does not immediately terminate when an exception occurs in the call to
PrintWriter.printit
returns, the virtual machine again raises the exception. The test program in
Example 11-19 demonstrates how the native method throws an exception
when the formatting string is not valid.
Example 11-17. Printf4.c
1. #include "Printf4.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_Printf4_fprint(JNIEnv* env, jclass cl,
37.
jobject out, jstring format, jdouble x)
38. {
39.
const char* cformat;
40.
char* fmt;
41.
jclass class_PrintWriter;
42.
jmethodID id_print;
43.
char* cstr;
44.
int width;
45.
int i;
46.
47.
if (format == NULL)
48.
{
49.
(*env)->ThrowNew(env,
50.
(*env)->FindClass(env,
51.
"java/lang/NullPointerException"),
52.
"Printf4.fprint: format is null");
53.
return;
54.
}
55.
56.
cformat = (*env)->GetStringUTFChars(env, format, NULL);
57.
fmt = find_format(cformat);
58.
59.
if (fmt == NULL)
60.
{
61.
(*env)->ThrowNew(env,
62.
(*env)->FindClass(env,
63.
"java/lang/IllegalArgumentException"),
64.
"Printf4.fprint: format is invalid");
65.
return;
66.
}
67.
68.
width = atoi(fmt);
69.
if (width == 0) width = DBL_DIG + 10;
70.
cstr = (char*)malloc(strlen(cformat) + width);
71.
72.
if (cstr == NULL)
73.
{
74.
(*env)->ThrowNew(env,
75.
(*env)->FindClass(env, "java/lang/OutOfMemoryError"),
76.
"Printf4.fprint: malloc failed");
77.
return;
78.
}
79.
80.
sprintf(cstr, cformat, x);
81.
82.
(*env)->ReleaseStringUTFChars(env, format, cformat);
83.
84.
/* now call ps.print(str) */
85.
86.
/* get the class */
87.
5.
1. import java.io.*;
2.
3. class Printf4
4. {
public static native void fprint(PrintWriter ps, String format,
double x);
6.
7.
static
8.
{
9.
System.loadLibrary("Printf4");
10.
}
11. }
1. import java.io.*;
2.
3. class Printf4Test
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.
/* This call will throw an exception--note the %% */
12.
Printf4.fprint(out, "Amount due = %%8.2f\n", amountDue);
13.
out.flush();
14.
}
15. }
Parameters:
env
cl
msg
Parameters:
env
Parameters:
Parameters: