JNI Tutorial
Invoke Java API from C++
A very simple tutorial that illustrates accessing Java API from C++. We use a simple Java class hierarchy that exposes a static method as well as regular methods, and then develop a sample C++ client that instantiates the Java class and invokes the methods.
The workflow for accessing Java API is as follows:
Create a JVM instance and specify the classpath as appropriate using
JNI_CreateJavaVM
Look up the Java Class definition using
JNIEnv::FindClass
Retrieve the constructor for the class using
JNIEnv::GetMethodID
Instantiate a new instance of the class using
JNIEnv::NewObject
Retrieve static methods using
JNIEnv::GetStaticMethodID
and instance methods usingJNIEnv::GetMethodID
Invoke static methods using
JNIEnv::CallStaticMethod
and instance methods usingJNIEnv::CallMethod
where result will depend upon the return type for the method (eg. Int, Boolean, Object, ...)You can invoke a super class variant of an instance method using
JNIEnv::CallNonvirtualMethod
. Note that thejmethodID
must be retrieved using the appropriate super-class definition.
A simple base java class that exposes static and regular methods. We will be accessing instances of this class and its methods from our C++ client.
File: src/java/Base.java
A simple child java class that regular methods as well as an over-ridden method. We will be accessing instances of this class and its methods from our C++ client.
File: src/java/Child.java
A simple C++ client that starts a JVM, looks up our sample Java class, instantiates an instance and invokes the methods defined.
File: src/cpp/client.cpp
A simple shell script used to build and run the samples. Note that we use javap to dump the method signatures for use in the C++ code.
A sample run should produce output similar to the following:
Java developers familiar with the Reflection API will have noticed the similarities between using Reflection to look up a class, use constructor to instantiate an instance etc. and the JNI API. They are indeed similar and follow a similar programming model.
Start JVM
The first step is to start a JVM instance with any parameters needed to properly initialise the JVM (system properties, class path, ...). For efficiency C++ client applications will generally use a single JVM instance (encapsulated suitably using RAII) and use it to load the Java API necessary. The process is illustrated in the `startJVM` method of the Client class.
Load Class and Instantiate Object
The next step usually is to load a class available in the classpath using JNIEnv::FindClass
. This step is equivalent to the java.lang.Class.forName
static method. These steps are illustrated in the init
method of the Client class.
Once you have a jclass
instance, you can invoke any static methods that are defined for that class without need to instantiate an object instance. You can look up constructors using the same technique as used to look up instance methods in the class. Constructors are always identified by the name <init>. This is slightly different from Reflection which separates constructor lookups from method lookups.
You can instantiate an object using the JNIEnv::NewObject
function, which is the equivalent of java.lang.reflect.Constructor.newInstance
method.
Invoke Methods
Static methods for a class are retrieved using JNIEnv::GetStaticMethodID
, while instance methods are retrieved using JNIEnv::GetMethodID
. Once you have a valid jmethodID
you invoke the method using JNIEnv::CallStatic<Type>Method
or JNIEnv::Call<Type>Method
depending upon whether the method is static or not.
Notes:
In our sample Client class, we promote the jclass
and jobject
instances to global references to avoid the instances being garbage collected before we are finished using them. Following RAII principles we release the references to these instances in the destructor. JNI client applications will in general keep a single global reference to Java class definitions and instantiate objects as necessary following the model used by the JVM.
com::sptci::jnitest::Client::staticIntMethod
- Invokes the static method defined in Base.javacom::sptci::jnitest::Client::intMethod
- Invokes the instance method defined in Child.javacom::sptci::jnitest::Client::booleanMethod
- Invokes the instance method defined in Base.javacom::sptci::jnitest::Client::stringMethod
- Invokes the over-ridden method defined in Child.javacom::sptci::jnitest::Client::nonVirtualStringMethod
- Invokes the base class variant of stringMethod defined in Base.java
The code was built and tested on Mac OS X Mavericks. The shell script shown uses path's used on OS X, and will need to be modified accordingly for other platforms.