getting started with the jni
TRANSCRIPT
Fullstack as a service
Getting Started with the JNIJava Native Interface
Kirill Kounik
- I am part of 12+ people strong Tikal's Android group.- Experience in startup and medium sized companies- 6.5 years in Sun Microsystems with JME technology- Java, client, low level- Graduate of Technion in Computer Science
WHO AM I?
Agenda
What JNI can doSimple exampleJNI basics
Native JNI functionsJava type mappingInspecting classes, calling Java methodsProcessing Exceptions
Goal
When JNI is useful - Pros
The standard Java class library does not support the platform-dependent features needed by the application.
You already have a library written in another language, and wish to make it accessible to Java code through the JNI.
You want to implement a small portion of time-critical code in a lower-level language.
When JNI is useful - Cons
You program is not platform independent anymoreYou are using low level language with all its drawbacksJNI call costs time http://stackoverflow.com/questions/13973035/what-is-the-
quantitative-overhead-of-making-a-jni-calljava version "1.7.0_09"OpenJDK Runtime Environment (IcedTea7 2.3.3) (7u9-2.3.3-1)OpenJDK Server VM (build 23.2-b09, mixed mode)Linux visor 3.2.0-4-686-pae #1 SMP Debian 3.2.32-1 i686 GNU/Linux
JNI access to JVM
Native JNI code leaves side by side with the JVM and has access to its structures. Create, inspect, and update Java objects (including arrays and strings).Call Java methods.Catch and throw exceptions.Load classes and obtain class information.Perform runtime type checking.Create threads visible to JVM
Native method in Java class
package jni;
public class CHelloWorld {
native String hello();
static {System.loadLibrary("jni_CHello");}
public static void main(String[] args) {CHelloWorld chw = new CHelloWorld();
System.out.println(chw.hello());}
}
Native method implementation
#include <jni.h>
/** Class: jni_CHelloWorld* Method: hello* Signature: ()Ljava/lang/String;*/
jstring Java_jni_CHelloWorld_hello(JNIEnv *env, jobject thiz)
{
return (*env)->NewStringUTF(env, "hello, world (from JNI)");
}
Native method implementation
jstring ← Java return type
Java_jni_CHelloWorld_hello( JNIEnv* env, jobject thiz )
Naming convention for JNI functions “this” object
reference
Pointer to JNI environment for JNI functions access
Method overloading
package jni;
public class CHelloWorld {
native String hello();native String hello(String what, int count);
. . .}
Method overloading/** Class: jni_CHelloWorld* Method: hello* Signature: ()Ljava/lang/String;*/
JNIEXPORT jstring JNICALL Java_jni_CHelloWorld_hello__ (JNIEnv *, jobject);
/** Class: jni_CHelloWorld* Method: hello* Signature: (Ljava/lang/String;I)Ljava/lang/String;*/
JNIEXPORT jstring JNICALL Java_jni_CHelloWorld_hello__Ljava_lang_String_2I(JNIEnv *, jobject, jstring, jint);
Resolving method names
A native method name is concatenated from the following components:
• the prefix Java_• a mangled fully-qualified class name• an underscore (“_”) separator• a mangled method name• for overloaded native methods, two underscores (“__”) followed by the
mangled argument signature
javah tool
The javah command conveniently generates C header and source files that are needed to implement native methods. (Generated files not really required)
$ javah -d .\src\native -cp .\bin\java jni.CHelloWorld
JNI native function arguments
The JNI interface pointer is the first argument to native methods. The JNI interface pointer is of type JNIEnv.
The second argument differs depending on whether the native method is static or nonstatic.
• The second argument to a nonstatic native method is a reference to the object.
• The second argument to a static native method is a reference to its Java class.
JNI native function arguments
The remaining arguments correspond to regular Java method arguments.
The native method call passes its result back to the calling routine via the return value
JNIEvn*
Reference to JNI environment, which lets you access all the JNI functions. Used for:
Create new objects
Access Fields inside Java classes
Invoke Java Methods.
It points to the thread’s local data, so it cannot be shared between threads.
Note on C++
#include <jni.h>
extern ”C” {
jstring Java_jni_CHelloWorld_hello(JNIEnv *env, jobject thiz) {
return env->NewStringUTF("hello, world (from JNI)");
}
}
Arguments of a primitive type passed by value
Java Type Native Type Constantsboolean jboolean JNI_FALSE, JNI_TRUEbyte jbytechar jcharshort jshortint jintlong jlongfloat jfloatdouble jdoublevoid void
jsize scalar values and sizes
Reference types
Reference types passed “by reference”. Here is native method hierarchy:
Accessing Stringsjstring Java_jni_CHelloWorld_hello__Ljava_lang_String_2I
(JNIEnv* env, jobject thiz, jstring what, jint count) {
char dest[30];
/* Obtain string characters */const char* str = (*env)->GetStringUTFChars(env, what, 0);
strcpy(dest, “hello, “);strncat(dest, str, 22);
/* Relase characters to avoid memory leak */(*env)->ReleaseStringUTFChars(env, what, str);
return (*env)->NewStringUTF(env, dest);
}
Local and Global References
• Every argument passed to JNI call is a local reference that is valid only for the duration of the call. This applies to all sub-classes of jobject, including jclass, jstring, and jarray.
• In order to get Global references:
jobject NewGlobalRef(JNIEnv *env, jobject obj);
• Global reference is live until it is not explicitly released
void DeleteGlobalRef(JNIEnv *env, jobject globalRef);
Local and Global References
• Do "not excessively allocate" local references. • Free local references them manually with DeleteLocalRef()• The implementation is only required to reserve slots for 16 local references, • if you need more than that you should either delete as you go or use EnsureLocalCapacity/PushLocalFrame to reserve more
Passing array arguments
/* private native long sumAll(int[] numbers); */
jlong Java_jni_CHelloWorld_sumAll(JNIEnv *env, jobject thiz, jintArray values_) {
jint *values = (*env)->GetIntArrayElements(env, values_, NULL);jsize len = (*env)->GetArrayLength(env, values_);jlong sum = 0;int i;
for (i = 0; i < len; i++) {sum += values[i];
}
(*env)->ReleaseIntArrayElements(env, values_, values, 0);
return sum;}
On class file structure
Goal: read or write class or instance variables from the JNI code
Goal: call java methods from the JNI code
Java VM type signatures:
Z booleanB byteC charS shortI intJ longF floatD double
Lfully-qualified-class; fully-qualified-class
[type type[]
(arg-types)ret-type method type
VM Signature example
Example Java method signature
long foo(int n, String s, int[] arr);
JVM Type signature:
(ILjava/lang/String;[I)J
javap tool
javap is Java Class File disassembler tool that comes to rescue
$ javap -s -p -cp .\bin\java jni.CHelloWorld
Accessing class/instance members
Find correct class object
Find member index, either method or field
Use correct instance object
Do method invocation or field access
Non-static method invocation
- Find class of your object
jclass cl = (*env)->GetObjectClass(env, textView);
- Find method
jmethodID methodId = (*env)->GetMethodID(env, cl, "setText", "(Ljava/lang/CharSequence;)V");
- Call your method using correct JNI function
(*env)->CallVoidMethod(env, textView, methodId, … );
Static method invocation
- Find correct class if needed
jclass cl = FindClass(env, "java/lang/String");
- Find static method
jmethodID methodId = (*env)->GetStaticMethodID(env, cl, "copyValueOf", "([C)Ljava/lang/String;");
- Call using correct invocation method
jobject o = (*env)->CallStaticObjectMethod(env, cl, methodId, /* NativeTypes */ … );
Accessing instance fields
- Find object’s class
- Get field id
jfieldID fieldId = (*env)->GetFieldID(env, clazz, "chars", "[C");
- Get/set field value using correct method
jobject o = (*env)->GetObjectField(env, instance, jfieldID );
(*env)->SetObjectField(env, instance, jfieldId, /* NativeType */ value);
Accessing static fields
- Find correct class if needed
- Find static field
jfieldID fieldId = (*env)->GetStaticFieldID(env, clazz, "count", "I");
- Get/set static field value using correct method
jint i = (*env)->GetStaticIntField(env, instance, jfieldID );
(*env)->SetStaticIntField(env, instance, jfieldId, /* NativeType */ 5);
Checking for errors
Calling most of JNI functions is not allowed if exception is pending and will cause crash. When there is a chance on an exception JNI must check for the exception state
jboolean ExceptionCheck(JNIEnv *env);
jthrowable ExceptionOccurred(JNIEnv *env);
void ExceptionClear(JNIEnv *env);
void ExceptionDescribe(JNIEnv *env);
Throwing an exception
jint Throw(JNIEnv *env, jthrowable obj);
jint ThrowNew(JNIEnv *env, jclass clazz, const char *message);
void FatalError(JNIEnv *env, const char *msg);
Usefull references
https://www.visualstudio.com/en-us/products/visual-studio-express-vs.aspx
http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/jniTOC.html
http://www.ibm.com/support/knowledgecenter/SSYKE2_7.0.0/com.ibm.java.lnx.70.doc/diag/understanding/jni.html