Previous | Next | Trail Map | Using the JNI | Java Native Interface Programming

Calling Java Methods

This section illustrates how to call Java programming language 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 programming language method. To make things a little more interesting, the Java programming language method again (recursively) calls the native method. This process continues until the recursion is five levels deep, at which time the Java programming language method returns without making any more calls to the native method. To help you see this, the Java programming language method and the native method print a sequence of tracing information.

Calling a Java Programming Language Method from Native Code

To see how native code calls a Java programming language 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 programming language 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 (non-static) method by following these three steps:

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 programming language class.

The method name is the Java programming language method name in UTF-8 form. Specify the method name for a 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 programming language method. The signature (I)V, for example, denotes a Java programming language 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 programming language 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 programming language 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 programming language 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

To eliminate the mistakes in deriving method signatures by hand, you can use the javap tool to print out method signatures. For example, by running:

javap -s -p Prompt
you can obtain 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 programming language types. The "-p" flag instructs javap to include private members.

Calling Java Programming Language 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 programming language class from which the method ID is derived. As long as the reference to the Java programming language 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 Programming Language Methods

The JNI provides several ways to pass arguments to a Java programming language 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 the arguments in a va_list and 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 programming language method you wish to invoke.

Calling Static Methods

You can call a static Java programming language method from your native code in a similar manner to calling an instance method. Call a static method by following these steps: If you compare instance method invocation functions to static 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 static method
   static int incDepth(int depth) {return depth + 1};
into Callback.java. We can call this static method incDepth from Java_Callback_nativeMethod by 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: It is rare that you will need to invoke the instance methods of a superclass. This facility is similar to calling a superclass method, say f, using:
super.f();
in the Java programming language.


Previous | Next | Trail Map | Using the JNI | Java Native Interface Programming