The Java TM Tutorial

| Start of Tutorial > Start of Trail > Start of Lesson | Search

Trail: Java Native Interface
Lesson: Interacting with Java from the Native Side

## Calling Java Methods

> This section illustrates how to call Java methods from native language methods. Our example program, `Callbacks.java`, invokes a native method. The native method then makes a call back to a Java method. To make things a little more interesting, the Java method again (recursively) calls the native method. This process continues until the recursion is five levels deep, at which time the Java method returns without making any more calls to the native method. To help you see this, the Java method and the native method print a sequence of tracing information.

### Calling a Java Method from Native Code

To see how native code calls a Java method, let us focus on the implementation of `Callbacks_nativeMethod`, which is implemented in `Callbacks.c`. This native method contains a call back to the Java method `Callbacks.callback`.

>     JNIEXPORT void JNICALL
>     Java_Callbacks_nativeMethod(JNIEnv *env, jobject obj, jint depth)
>     {
>         jclass cls = (*env)->GetObjectClass(env, obj);
>         jmethodID mid = (*env)->GetMethodID(env, cls, "callback", "(I)V");
>         if (mid == 0) {
>             return;
>         }
>         printf("In C, depth = %d, about to enter Java\n", depth);
>         (*env)->CallVoidMethod(env, obj, mid, depth);
>         printf("In C, depth = %d, back from Java\n", depth);
>     }

You can call an instance method by following these three steps:

1. Your native method calls the JNI function `GetObjectClass`, which returns the Java class object that is the type of the Java object.
2. Your native method then calls the JNI function `GetMethodID`, which performs a lookup for the Java method in a given class. The lookup is based on the name of the method as well as the method signature. If the method does not exist, `GetMethodID` returns zero (0). An immediate return from the native method at that point causes a `NoSuchMethodError` to be thrown in the Java application code.
3. Lastly, your native method calls the JNI function `CallVoidMethod`. The `CallVoidMethod` function invokes an instance method that has `void` return type. You pass the object, method ID, and the actual arguments to `CallVoidMethod`.

### Forming the Method Name and Method Signature

The JNI performs a symbolic lookup based on the method's name and type signature. This ensures that the same native method will work even after new methods have been added to the corresponding Java class.

The method name is the Java method name in UTF-8 form. Specify the constructor of a class by enclosing the word `init` within angle brackets (this appears as "<init>").

Note that the JNI uses the method signature to denote the return type of a Java method. The signature `(I)V`, for example, denotes a Java method that takes one argument of type `int` and has a return type `void`. The general form of a method signature argument is:

>     "(argument-types)return-type"

The following table summarizes the encoding for the Java type signatures:

> ##### Java VM Type Signatures

> Signature | Java Programming Language Type
> | `Z` | `boolean` | `B` | `byte` | `C` | `char` | `S` | `short` | `I` | `int` | `J` | `long` | `F` | `float` | `D` | `double` | `L` fully-qualified-class; | fully-qualified-class | `[` type | type`[]` | `(` arg-types `)` ret-type | method type

For example, the `Prompt.getLine` method has the signature:

>     (Ljava/lang/String;)Ljava/lang/String;

`Prompt.getLine` takes one parameter, a Java `String` object, and the method type is also `String`.

The `Callbacks.main` method has the signature:

>     ([Ljava/lang/String;)V

The signature indicates that the `Callbacks.main` method takes one parameter, a Java `String` object, and the method type is void.

Array types are indicated by a leading square bracket ([) followed by the type of the array elements.

### Using `javap` to Generate Method Signatures

The Java class file disassembler tool, `javap`, helps you to eliminate the mistakes that can occur when deriving method signatures by hand. You can use the `javap` tool to print out member variables and method signatures for specified classes. Run the `javap` tool with the options `-s` and `-p` and give it the name of a Java class, as follows:

>     javap -s -p Prompt

This gives you the following output:

>     Compiled from Prompt.java
>     class Prompt extends java.lang.Object
>         /* ACC_SUPER bit set */
>     {
>         private native getLine (Ljava/lang/String;)Ljava/lang/String;
>         public static main ([Ljava/lang/String;)V
>         <init> ()V
>         static <clinit> ()V
>     }

The "-s" flag informs `javap` to output signatures rather than normal Java types. The "-p" flag instructs `javap` to include private members.

### Calling Java Methods Using Method IDs

When you invoke a method in the JNI, you pass the method ID to the actual method invocation function. Obtaining a method ID is a relatively expensive operation. Because you obtain the method ID separately from the method invocation, you need only perform this operation once. Thus, it is possible to first obtain the method ID one time and then use the method ID many times at later points to invoke the same method.

It is important to keep in mind that a method ID is valid only for as long as the class from which it is derived is not unloaded. Once the class is unloaded, the method ID becomes invalid. As a result, if you want to cache the method ID, be sure to keep a live reference to the Java class from which the method ID is derived. As long as the reference to the Java class (the `jclass` value) exists, the native code keeps a live reference to the class. The section Local and Global References explains how to keep a live reference even after the native method returns and the `jclass` value goes out of scope.

### Passing Arguments to Java Methods

The JNI provides several ways to pass arguments to a Java method. Most often, you pass the arguments following the method ID. There are also two variations of method invocation functions that take arguments in an alternative format. For example, the `CallVoidMethodV` function receives all its arguments in one `va_list` type argument. A `va_list` type is a special C type that allows a C function to accept a variable number of arguments. The `CallVoidMethodA` function expects the arguments in an array of `jvalue` union types. The array of `jvalue` union types are as follows:

>     typedef union jvalue {
>         jboolean z;
>         jbyte    b;
>         jchar    c;
>         jshort   s;
>         jint     i;
>         jlong    j;
>         jfloat   f;
>         jdouble  d;
>         jobject  l;
>     } jvalue;

In addition to the `CallVoidMethod` function, the JNI also supports instance method invocation functions with other return types, such as `CallBooleanMethod`, `CallIntMethod`, and so on. The return type of the method invocation function must match with the type of the Java method you wish to invoke.

### Calling Class Methods

You can call a Java class method from your native code in a similar manner to calling an instance method. Call a class method by following these steps:

- Obtain the method ID using the JNI function `GetStaticMethodID` rather than the function `GetMethodID`.
- Pass the class, method ID, and arguments to the family of class method invocation functions: `CallStaticVoidMethod`, `CallStaticBooleanMethod`, and so on.

If you compare instance method invocation functions to class method invocation functions, you will notice that instance method invocation functions receive the object, rather than the class, as the second argument following the `JNIEnv` argument. For example, suppose we add a `incDepth` class method into `Callback.java`.

>         static int incDepth(int depth) {return depth + 1};

We can call this class method `incDepth` from `Java_Callback_nativeMethod` using the following JNI functions:

>     JNIEXPORT void JNICALL
>     Java_Callbacks_nativeMethod(JNIEnv *env, jobject obj, jint depth)
>     {
>         jclass cls = (*env)->GetObjectClass(env, obj);
>         jmethodID mid = (*env)->GetStaticMethodID(env, cls, "incDepth", "(I)I");
>         if (mid == 0) {
>             return;
>         }
>         depth = (*env)->CallStaticIntMethod(env, cls, mid, depth);
>         ...

### Calling Instance Methods of a Superclass

You can call instance methods defined in a superclass that have been overridden in the class to which the object belongs. The JNI provides a set of `CallNonvirtual<type>Method` functions for this purpose. To call instance methods from the superclass that defined them, you do the following:

- Obtain the method ID from the superclass using `GetMethodID` rather than `GetStaticMethodID`.
- Pass the object, superclass, method Id, and arguments to the family of nonvirtual invocation functions: `CallNonvirtualVoidMethod`, `CallNonvirtualBooleanMethod`, and so on.

It is rare that you will need to invoke the instance methods of a superclass. This facility is similar to calling a superclass method, such as `f`, in Java using the following construct:

>     super.f();

| Start of Tutorial > Start of Trail > Start of Lesson | Search