Programmming in C/C++ with the Java Native Interface

Matthew Mead

Contents

Introduction
Exercise 1
Exercise 2
Exercise 3

Introduction

These exercises demonstrate a few techniques used with JNI programming.

Exercise 1

This exercise demonstrates how to pass an argument from Java to a function implemented in C++.

Educational goals

Implement a C++ function that calculates the area of circle and call it from Java code. A calculation is performed in the C++ function and the result is passed back to the Java code. The C++ function implements a native method:

   double calculateAreaOfCircle(int radius);
which, as you would expect, calculates the area of a circle given its radius.

Please follow the link to the exercise 1 page.

Exercise 2

This exercise demonstrates how to call a Java method from C++ code.

Educational goals

When a native function is called, two "hidden" arguments are passed in to the function along with the programmer's arguments. Recall the native method from the HelloWorld example:

JNIEXPORT void JNICALL Java_HelloWorld_displayMessage(JNIEnv *env, jobject obj) 
The two parameters, env and obj were not passed by the caller and were not used by the function, yet they are required in the definition. The first parameter is a pointer to the Java environment (JVM) at runtime and the second is a reference to the Java object that called this native method. In the HelloWorld example the object was an instance of the HelloWorld class.

In this exercise we will need to use these arguments in order to call a Java method from C++. There are basically three steps that must be taken in order for C++ to call a Java method. The sample calls below assume that there is a Java method called calculateAreaOfCircle that takes a double as a parameter and returns a double.

  1. Get the class associated with the calling object by invoking a method of the env object called GetObjectClass and passing the object to the function.
    jclass cls = env->GetObjectClass(obj);
    
  2. Get the ID for the method you wish to call by invoking a method of the env object called GetMethodID and passing the class of the object, the name of the method, and the method's signature.
    jmethodID mid = env->GetMethodID(cls, "calculateAreaOfCircle", "(D)D");
    
  3. Invoke the method by calling CallXXXXMethod, where XXXX is the return type of the Java method. In this example, the function returns a double. You pass the object and the method ID followed by the actual parameters to the function.
jdouble area = env->CallDoubleMethod(obj, mid, radius);
Please follow the link to the exercise 2 page.

Exercise 3

This exercise demonstrates how to access Java fields from C++ code.

Educational goals

Accessing Java fields from native code is similar to invoking Java methods from native code; you use JNI API functions. Suppose you have a Java class:

class MyClass
{
  public int x;         // non-static public field
  static public int y;  // static public field
}
and assume you have a C++ function such as this:
JNIEXPORT void JNICALL Java_MyClass_SomeFunc(JNIEnv *env, jobject obj) 
To access a non-static field from C++, you would need to perform these calls within the native method:

  1. Get the class associated with the calling object by invoking a method of the env object called GetObjectClass and passing the object to the function.
    jclass cls = env->GetObjectClass(obj);
    
  2. Get the ID for the non-static field you wish to access by invoking a method of the env object called GetFieldID and passing the class of the object, the name of the field, and the field's signature. The signature for a field is its data type.
    jfieldID fid = env->GetFieldID(cls, "x", "I");
    
  3. To get the value from the field, you call GetXXXXField, where XXXX is the type of the field. You need to supply the object and the field ID. In this example, the field is an integer.
    jint x = env->GetIntField(obj, fid);
    
To access a static field from C++, you would need to perform step one above, but steps two and three are slightly different:

  1. Same as above.

  2. Get the ID for the static field you wish to access by invoking a method of the env object called GetStaticFieldID and passing the class of the object, the name of the static field, and the field's signature.
    jfieldID fid = env->GetStaticFieldID(cls, "y", "I");
    
  3. To get the value from the field, you call GetStaticXXXXField, where XXXX is the type of the field. You need to supply the class (static fields are class fields) and the field ID. In this example, the field is an integer.
    jint x = env->GetStaticIntField(cls, fid);
    
To set Java fields from C++, you call the corresponding functions for static and non-static fields:

  1. Same as above

  2. Same as above

  3. To set the field's value, you call SetXXXXField or SetStaticXXXXField, where XXXX is the type of the field. In the example below, value is an integer.
    env->SetIntField(obj, fid, value);         // for non-static fields
    env->SetStaticIntField(cls, fid, value);   // for non-static fields
    
Please follow the link to the exercise 3 page.
Copyright © 1998 Sergio Antoy. All Rights Reserved
February 99, version 020199