JNI details
looking into JNI in details
Declaring a native method in Java
to be implemented in a lower-level language
"Native": binary is platform dependent
Use native keyword in Java
native int simpleNativeMethod(int a, double b);
Declaring a JNI header in C
Methods to be declared in a header:
JNIEXPORT int JNICALL
Java_fully_qualified_name_of_Class_simpleMethodNativeMethod(JNIEnv *, jobject, jint, jdouble);
Must include "jni.h" to use JNI types and macros - this header can be found in $JAVA_HOME/include
Header can be machine-generated
javah -jni - classpath /path/to/bytecode -force -o generatedHeader.h fully.qualified.name.of.Class
JavaH - Java Header Generator - binary can be founr iin $JDK_HOME/bin
Implementing a native method in C
Include the created JNI header to a C source file and implement tha JNI functions (do not forget to include "jni.h" as well).
Cast JNI types to C types
Tun your algorithms
Cast C types back to JNI types for result return
Mapping between Java, JNI and C
Mapping between Java and JNI is performed automatically by the JVM
Mapping between types JNI and C to be ensures by type casting - however, most ot JNI types are only "renaming" of their C counterpart (a.k.a typedef).
Mapping Java types to JNI types
Primitive types are passed by copy to the native functions.
Java primitive type
JNI type
Format
boolean
jboolean
unsigned 8 bits
byte
jbyte
singed 8 bits
char
jchar
unsigned 16 bits
short
jshort
signed 16 bits
int
jint
signed 32 bits
long
jlong
signed 64 bits
float
jfloat
32 bits
double
jdouble
64 bits
void
void
N/A
Composed types are passed by reference to the native functions
Java composed type
JNI type
String
jstring
Class
jclass
Object[]
jobjectArray
boolean[]
jbooleanArray
byte[]
jbyteArray
char[]
jcharArray
short[]
jshortArray
int[]
jintArray
long[]
jlongArray
floa[]
jfloatArray
double[]
jdoubleArray
Throwable
jthrowable
Type hierarchy:
all array types in JNI are subtype of jarray
all object types in JNI are subtypes of jobject
Mapping JNI types to C/C++ types
JNI type
Native type
jboolean
unsigned char
jbyte
signed char
jchar
unsigned short
jshort
short
jint
int
jlong
long or long long
jfloat
float
jdouble
double
void
void
types jbyte, jint, jlong are architecture-dependent, so the mapping may vary
boolean types do not exist in C, but integer types can do the trick with the existing JNI definitions:
#define JNI_FALSE 0
#define JNI_TRUE 1
Composed types have to be mapped manually (no direct cast can be applied).
The jobject must be explored, each field read and copied to an external structure.
Use of the JNIEnv object to access (read, write, execute, map) Java abstractions from within JNI
Mapping JNI Arrays to C/C++
JNI arrays are directly mapped to C arrays
jdouble * arrayOfJDoubles = jniEnv->GetDoubleArrayElements(aJDoubleArray,0);
jlong * arrayOfJLongs = jniEnv->GetLongArrayElements(aJLongArray,0);
jint * arrayOfJInts = jniEnv-GetIntArrayOfElemnts(aJIntArray,0);
However manually mapping has to be performed if one wants to take advantage of C++ bool type:
bool * anArrayOfBools = new bool[n];
jboolean * arrayOfJBooleans = jniEnv->GetBooleanArrayElements(aJBooleanArray,0);
for (int k = 0; k < n; k++)
anArrayOfBools[k] = arrayOoJBooleana[k] == JNI_TRUE;
Unwrapping JNI objects in C/C++
Get class of jobject
Use class to get field ID using the field information (name and type).
Get object field using the object and field ID
Repeat these steps in the retrived field is an object itself; map the field to C/C++ otherwise
jclass aJClass = jniEnv->GetObjectClass(aJObject);
jfieleID aJFieldID = jniEnv->GetFieldID(aJClass, "fieldName", "[Lfully/qualified/name/of/Class;");
if (aJFieldID == NULL) {
//this means requested field does not exist!
}
jobject objectField = jniEnv->GetObjectField(aJObject, aJFieldID);
JNI Type Signatures
Java Type
Type Signature
boolean
Z
byte
B
char
C
short
S
int
I
long
J
float
F
double
D
Class
Lfully/qualified/name/of/Class
Array of 'type'
[type
Accessing JNI object fields in C/C++
All composed objects are unwrapped to jobject - they should be explicitly casted to a proper JNI subtype, if applicable (e.g. jbooleanArray, jintArray)
Fields of the primitive type must be accessed using each function counterpart
GetObjectField GetIntField GetFloatField GetBooleanField GetLongField GetDoubleField
Performing side effects in JNI
As Java objects passed by reference to JNI, changes are reflected in the Java runtime.
Native methods can cause side effects:
modifying an object field
modifying a Java object field
Get class of jobject
use class to get field ID using the fields information (name and type)
set a value to the object field using the object, the field ID and the value to be set
modifying an int field
jclass aJClass = jniEnv->GetObjectClass(aJObject);
jfieldID aJFieldID = jniEnv->GetFieldID(aJClass, "anIntField", "I");
if (aJFieldID == NULL) {
//this means the requested field does not exist!
}
jobject objectField = jiniEnv->SetIntField(aJObject,aJFieldID,anyIntValue);
cd
modifying an int array field
jclass aJClass = jniEnv->GetObjectClass(aJObject);
jfieldID aJFieldID = jniEnv->GetFieldID(aJClass, "anIntArrayField", "[I");
if (aJFieldID == NULL) {
//this means the requested field does not exist!
}
jintArray jniArrayField = (jintArray) jniEnv->GetObjectField(aJObject,aJFieldID);
jiniEnv->SetIntArrayRegion(jniArrayField,initialIdx, finalIdx, anArrayOfIntValues);
cd
modyfying an int matrix field
jclass aJClass = jniEnv->GetObjectClass(aJObject);
jfieldID aJFieldID = jniEnv->GetFieldID(aJClass, "anIntMateixField", "[[I");
jobjectArray jniMatrixField = (jobjectArray)
jniEnv->GetObjectField(aJObject,aJFieldID);
for (int k = 0; k < n; k++) {
jintArray jniArrayField = (jintArray)
jniEnv->GetObjectArrayElement(jniMatricField, k);
jniEnv->SetIntArrayRegion(jniArrayfield, initialIdx, finalIdx, anArrayOfIntValues);
}
invoking an object method
Get class of jobject
use class to get method ID using the method information (name and type signature)
call the object method using the object, the method ID and set of arguments (if any)
jclass aJClass = jniEnv->GetObjectClass(aJObject);
jmethodID aJMethodID = jniEnv->GetMEthodID(aJclass, "methodName", "(IFJ)D");
if (aJMethodID == NULL {
//requested methond does not exist
}
jdouble result = jniEnv->CallDoubleMethod(aJObject, aJMethodID,
jintArg, jfloatArg, jlongArg);
Method signatures are in the following format: "(contiguous-list-of-argument-types)return-type"
long simpleMethod(int n, String s, float[] ff);
(ILjava/lang/String;[F)J
double anotherMethod(String[] dict, int[][] map);
([Ljava/lang/String;[[I)D
Last updated
Was this helpful?