![]() ![]() ![]() ![]() |
Implementing Native Methods |
As you saw on the previous page, the Java runtime system passes an extra argument to native methods as the first argument in the argument list. Let's investigate this a little further, then look into how you can pass your own arguments to a native method.The Automatic Parameter
TheInputFile_close()
andOutputFile_close()
function both accept one argument: the automatic parameter passed into the function automatically by the Java runtime system.Notice first the declaration for the automatic parameter in the// in InputFileImpl.c void InputFile_close(struct HInputFile *this) . . . // in OutputFileImpl.c void OutputFile_close(struct HOutputFile *this) . . .InputFile_close()
function signature. The InputFile object, that is, the object whoseInputFile_close()
is being called, is passed into the native method through a handle to a structure. The name of the structure is comprised of the capital letter 'H' (presumably for "Handle") and the name of the Java class.You can use this handle to reference the object's member variables from within the native method. Missing Page covers this in detail.
When you write a native method that accepts some object as an argument, you will notice that those objects are also passed into the native method as a handle to a structure. It's the same mechanism used by the runtime system to pass in the automatic parameter. More about this later on this page.
Passing Simple Data Types
You can pass simple data types, integers, boolean, floats, and so on into a native method. InputFile'sread()
method and OutputFile'swrite()
method both take an integer argument:The first statement declares that the// in InputFile.java public native int read(byte b[], int len); // in OutputFile.java public native int write(byte b[], int len);read()
method's second argument is an integer. The second statement declares thatwrite()
method's second argument is also an integer. You can ignore the first argument for now, it's covered later on this page.Besides integers, you can also pass floats, booleans, doubles, chars and other simple data types to a native method. On the native language side, these simple data types are mapped to the closest matching native language data type. For example, on the C side of our example, the function signature for the
InputFile_read()
function looks like this:Ignore the middle argument for now--it's covered later.// in InputFileImpl.c long InputFile_read(struct HInputFile *this, HArrayOfByte *buffer, long count)Notice that the function accepts the integer argument as a
long
. That's because C long integers are the nearest match to Java integers. This table shows the correspondence between types in Java and types in C. You must use the mappings in this table to get the correct data into a native method written in C.Similarly, the
OutputFile_write()
function accepts along
where a Javaint
was passed in:Now that you've got the simple data passed into the method, it's presumable that you'd like to use it. Well, you can use these simple data type arguments just as you would any other C variable: by name. For example, this code snippet that occurs in both// in OutputFileImpl.c long OutputFile_write(struct HOutputFile *this, HArrayOfByte *buffer, long count)InputFile_read()
andOutputFile_write()
uses thecount
argument to set the number of bytes to be read or written.if (len < count) { actual = len; } else { actual = count; }Note that simple data type arguments are passed to the native function by value: that is, if you modify one of the arguments within the native function, the change will not affect the calling Java method. If you want to return a value or set of values through a native method's arguments, see Missing Page.
Passing Complex Data Types
In addition to parameters of simple types, you can pass complex data types into native methods as well. Complex data types include arrays, strings, and objects all of which are first-class Java objects. Java objects are passed into a native method as a handle to a Cstruct
. Indeed, the Java runtime system uses this mechanism to pass in the Java object whose native method is being called. You saw this in action in the The Automatic Parameter section earlier on this page.InputFile's
read()
method accepts a byte array--the method reads bytes from the input file and returns them through this array. OutputFile'swrite()
method also accepts a byte array--the method writes the bytes contained in this array to the output file.The Java side of a native method that takes an object argument is straightforward: simply declare the method as though it were a "regular" Java method:
The first statement declares that the first argument to the// in InputFile.java public native int read(byte b[], int len); // in OutputFile.java public native int write(byte b[], int len);read()
method is an array of bytes. The second statement declares that the first argument to thewrite()
method is also an array of bytes.On the C side, the declaration for the two functions that implements the
read()
andwrite()
methods look like this:The second argument to both functions is the byte array argument passed in from the Java side. As you know Java arrays are first class objects. Thus, the array, like an object, is passed in as a handle to a C// in InputFileImpl.c long InputFile_read(struct HInputFile *this, HArrayOfByte *buffer, long count) // in OutputFileImpl.c long OutputFile_write(struct HOutputFile *this, HArrayOfByte *buffer, long count)struct
. The structure name is comprised of the capital letter 'H' and the name of the class. Notice, however, that the fully qualified name of the class is used. In the case of arrays, the fully qualified name of the class isArrayOf
plus the data type of the elements contained within the array. So, the fully qualified class name of an array of bytes isArrayOfByte
, and the fully qualified class name of an array of integers isArrayOfInt
.The fully qualified name of other classes include the name of the package that the class is declared in. Thus a String is passed in as a handle to a Hjava_lang_String structure. An Object is passed in as a handle to a Hjava_lang_Object structure, a Stack would be passed in as a handle to a Hjava_util_Stack structure, and so on.
Now that you've got a handle in the C function to a Java object, what can you do with it? You can use it to access the object's instance and class [PENDING: is it true you can get class vars too?] variables. This is covered in Missing Page.
A Word of Warning
Due to the background activities of the garbage collector, you must be careful to maintain references to all objects passed into a native method to prevent the object from being moved to another location on the heap. The reference must be on the C stack (can't be a global variable). Typically, you don't need to worry about this because the argument passed into the native method is normally sufficient to keep the garbage collector informed of a reference to a particular object. However, if you are working with global variables or doing unorthodox pointer manipulations, keep in mind that the garbage collector needs to be kept apprised of any references in a native method to Java objects.Note that the garbage collector does not recognize a pointer to the middle of an object as a reference to that object. So referenes that you maintain to an object must point the beginning of the object!
![]() ![]() ![]() ![]() |
Implementing Native Methods |