JNI-TL is a lightweight С++ library which facilitates using java objects from C++ code. It’s written on top of standard Java Native Inteface 6.0 API (comes with including <jni.h>
header file) and merely comprises a few handy wrappers what makes using JNI API easier.
For further reading it’s expected that reader is familiar with technical aspects of JNI API.
Basic wrappers for jclass
and jobject
types are represented by corresponding Class
and Object
C++ classes.
JNIEnv *jenv; jclass jcls; jobject jobj;
Class clazz(jenv, jcls); Object object(clazz, jobj);
Object
type inherits Class
type and both of them leverage accessing Java object’s or/and class’s methods and fields. Objects of these classes basically act as maps of fields and methods which might be accessed by their definitions.
object[<method>](<arguments>)
<value> = object[<field>] object[<field>] = <value>
the same for Class
object
clazz[<method>](<arguments>)
<value> = clazz[<field>] clazz[<field>] = <value>
Example:
jint value = object[Field<jint>("field_name")]; object[Field<jint>("field_name")] = value;
object[Method<jboolean>("equals", Args<jobject>("java.lang.Object")](object);
The Field
definition is represented by the name and its type. And when jobject
type is a case, object’s class name is must.
<Specifier>::Field<jtype>("<field_name>")
<Specifier>::Field<jobject>("<field_name>", "<fully.qualified.field.class.name>")
Like Field
the Method
definition requires both name and type of returned value as well as object’s class name when method returns jobject
type.
<Specifier>::Method<returned-jtype>("<method_name>", [, <arguments>])
<Specifier>::Method<jobject>("<method_name>"[, <arguments>], "<fully.qualified.returned.class.name>")
In addition method’s arguments description must be defined whenever method has parameters to be passed to.
Args is a template with variadic count of template arguments. Each template argument’s type defines the corresponding parameter’s type passed to the method and all jobject
parameters requires corresponding object’s class name to be passes into Args class constructor.
Args<[jtype|jobject,]...>([<fully.qualified.class.name.for.jobject.parameter>], ...)
There are several kind of <field>s
and <method>s
specified by prefix
Field type | Description | Applicable to | |
---|---|---|---|
Class |
Object |
||
Class::Static::Field |
Accessing class’s static fields | yes | yes |
Object::Static::Field |
The same as Class::Static::Field |
yes | yes |
Object::Field |
Accessing object’s fields | no | yes |
Method type | Description | Applicable to | |
Class |
Object |
||
Class::Static::Method |
Accessing class’s static methods | yes | yes |
Class::Method |
Accessing non-virtual object’s methods of specified class | no | yes |
Object::Static::Method |
The same as Class::Static::Method |
yes | yes |
Object::Method |
Accessing object’s methods | no | yes |
Array template class wraps java built-in container object and is specified by type of array elements.
JNIEnv *jenv; jtypeArray jarr;
Array<jtype> array(jenv, jarr);
Access Array
elements
There is difference in accessing elements of jobject
type and elements of primitive types
- accessing jobject elements
Array<jstring> array(...);
jstring si = array[i]; array[i] = si;
- accessing all primitive elements
Array<jtype> array(...); bool copyBack = false; # Tells whether copy all changes back to array
Array<jtype>::Elements elements = array.elements(copyBack); jtype value = elements[i]; elements[i] = value;
elements.commit(); # Manually copy elements back to array
Writing elements back happens on Array::Elements
destructor and only when elements have been acquired with a raised copyBack flag or might be fired manually by calling commit
method on Elements
object.
- accessing all primitive elements with critical restrictions
Array<jtype>::Critical::Elements elements = array.critical(copyBack);
jtype value = elements[i];
elements[i] = value;
- accessing some
Region
of primitive elements
RequestingRegion
allows to access small amount of elements. An index counts elements in the range not from array beginning.
Array<jtype>::Region::Elements elements = array.region(Region(start, length), copyBack);
jtype value = elements[i];
elements[i] = value;
String
class allows to convert from jstring
to std::string
and vice versa.
JNIEnv *jenv; jstring jstr;
String string(jenv, jstr); std::string stdstr = string; jstr = String(jenv, "some native text");
// TBI
- Add compilation of
jni_tl.cpp
file into an appropriate project #include <jni_tl.h>
header file- Whenever it’s possible declare
using namespace JNI;
since all classes are defined under thisnamespace
.
Accessing Fields
or Methods
by subscript operator of Class
or Object
comprises two calls of JNI API functions:
GetFieldID/GetStaticFieldID/GetMethodID/GetStaticMethodID
Get<jtype>Field/Set<jtype>Field/CallStatic<jtype>Method/Call<jtype>Method/CallNonvirtual<jtype>Method
Example:
jenv->Set<jtype>Field(object, jenv->GetFieldID(clazz, "field_name", "field_signature"), <value>);
Suppose the field is accessed several times
for (int i = 0; i < N; ++i) {
jenv->Set<jtype>Field(object, jenv->GetFieldID(clazz, "field_name", "field_signature"), <value>);
}
In this case code might be optimized
jfieldID fieldID = jenv->GetFieldID(clazz, "field_name", "field_signature");
for (int i = 0; i < N; ++i) {
jenv->Set<jtype>Field(object, fieldID, <value>);
}
In the similar way might be optimized accesing to Fields
and Methods
Object::Field<jtype>::ID fieldID = clazz(Object::Field<jint>("field_name"));
for (int i = 0; i < N; ++i) {
object[fieldID] = <value>;
}
The best way access Java object of some class is through a corresponding wrapper which implements necessary functionality and hides JNI specifics. In the most cases wrapper merely inherits JNI::Object and provides a bunch of necessary functions.
The next example shows how to make wrapper for a part of java.lang.Integer
java class and than use it.
#include "jni_tl.h"
using namespace JNI;
struct Integer : Object {
Integer(const Env &env, jobject object = 0) : Object(Class("java.lang.Integer"), object) {
MIN = (*this)[Static::Field<jint>("MIN_VALUE")]; MAX = (*this)[Static::Field<jint>("MAX_VALUE")];
hashCodeID = (*this)(Method<jint>("hashCode")); bitCountID = (*this)(Static::Method<jint>("bitCount", Args<jint>())); }
jint hashCode() const { return (*this)[hashCodeID](); }
jint bitCount(jint value) const { return (*this)[bitCountID](value); }
jint max() const { return MAX; } jint min() const { return MIN; }
private: jint MAX, MIN;
private: Method<jint>::ID hashCodeID; private: Static::Method<jint>::ID bitCountID; };
void UsingIntegerCodeSnippet(JNIEnv *jenv, jobject jinteger) { Integer integer(jenv, jinteger); jint bits = integer.bitCount(0xF); jint hash = integer.hashCode(); jint max = integer.max(); jint min = integer.min(); }