33
Android JNI/NDK

Android JNI

Embed Size (px)

DESCRIPTION

Deep Dive into Android JNI

Citation preview

Page 1: Android JNI

Android JNI/NDK

Page 2: Android JNI

Android JNI● Why JNI?● What is NDK?● Why NDK?● Deep into JNI world.

Page 3: Android JNI

Why JNI?Android is put together of about equal part Java and C.we need an easy way to bridge between these two totally different worlds.Java offers Java Native Interface(JNI) as a framework connecting the world of Java to the native code.

Page 4: Android JNI

More About JNIJNI is part of Dalvik VM, which allows native code to access java environment. Like accessing java objects and its methods, members etc.JNI also facilitates accessing and invoking of native methods from Java code. JNI Comes with Primitive and reference types.Primitive types can be manipulated directly as these are equivalent to native c.c++ data typesBut you need special helper functions to manipulate JNI reference types.

Page 5: Android JNI

What is NDK?NDK is a toolchainCross-compiler, linker, what you need to build for ARM, x86, MIPS, etc.NDK provides a way to bundle lib.so into your APKThe native library needs to be loadable in a secure way.NDK "standardizes" various native platformsIt provides headers for libc, libm, libz, lliibblloogg, lliibbjjnniiggrraahhiiccss, OpenGL/OpenSL ES,JNI headers, minimal C++ support headers, and Android native app APIs

Page 6: Android JNI

NDK in Action

Page 7: Android JNI

Why NDK/JNI?For performance

Sometimes, native code still runs faster.For legacy support

You may have that C/C++ code you’d like to use in your app.For access to low-level libraries

I n a rare case when there is no Java API to do something.For cross-platform developmentBut Adding JNI to your app will make it more complex.

Page 8: Android JNI

Build Process

Page 9: Android JNI

Deep Into JNI world

Page 10: Android JNI

Loading Native Libraries and Registering Native Methods

Native code is usually compiled into a shared library and loaded before the native methods can be called. All the Native methods are declared with native keyword in java.

static { //use either of the two methods below System.loadLibrary(“nativelib"); System.load("/data/data/cookbook.chapter2/lib/ libNative.so"); }

Page 11: Android JNI

Registering Native Methods JNIEnv Interface Pointer:

Every native method defined in native code at JNI must accept two input parameters, the first one being a pointer to JNIEnv. The JNIEnv interface pointer is pointing to thread-local data, which in turn points to a JNI function table shared by all threads. This can be illustrated using the following diagram:

Page 12: Android JNI

JNIEnvGateway to access all predefined JNI functions.Access Java FieldsInvoke Java Methods.It points to the thread’s local data, so it cannot be shared.It can be accessible only by java threads.Native threads must call AttachCurrentThread to attach itself to VM and to obtain the JNIEnv interface pointer.RegisterNatives : Two ways: 1. Using Javah tool. 2. Using RegisterNative function.

Page 13: Android JNI

Using RegisterNative MethodPrototype:

jint RegisterNatives(JNIEnv *env, jclass clazz, const JNINativeMethod *methods, jint nMethods);

The clazz argument is a reference to the class in which the native method is to be registered. The methods argument is an array of the JNINativeMethod data structure. JNINativeMethod is defined as follows:

typedef struct { char *name; char *signature; void *fnPtr; } JNINativeMethod;

name indicates the native method name, signature is the descriptor of the method's input argument data type and return value data type, and fnPtr is the function pointer pointing to the native method. The last argument, nMethods of RegisterNatives, indicates the number of methods to register. The function returns zero to indicate success, and a negative value otherwise.

Page 14: Android JNI

JNI_OnLoad Invoke when the native library is loaded. It is the right and safe place to register the native methods before

their execution.

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* pVm, void* reserved) { JNIEnv* env; if ((*pVm)->GetEnv(pVm, (void **)&env, JNI_VERSION_1_6)) { return -1; } // Get jclass with env->FindClass. // Register methods with env->RegisterNatives. return JNI_VERSION_1_6; }

Page 15: Android JNI

JNI Datatypes

Page 16: Android JNI

Manipulating strings in JNI Strings are somewhat complicated in JNI, mainly because Java strings and C strings are internally

different. Java programming language uses UTF-16 to represent strings. If a character cannot fit in a 16-bit

code value, a pair of code values named surrogate pair is used C strings are simply an array of bytes terminated by a null character

The Unicode Standard is a character coding system designed to support the worldwide interchange, processing, and display of the written texts of the diverse languages and technical disciplines of the modern world. In addition, it supports classical and historical texts of many written languages.

Few JNI String Functions: jstring NewStringUTF(JNIEnv *env, const char *bytes); void GetStringUTFRegion(JNIEnv *env, jstring str, jsize start, jsize

len, char *buf); void ReleaseStringUTFChars(JNIEnv *env, jstring string, const char

*utf); const jbyte * GetStringUTFChars(JNIEnv *env, jstring string,

jboolean *isCopy);

Page 17: Android JNI

Manipulating Objects in JNIjobject AllocObject(JNIEnv *env, jclass clazz); jobject NewObject(JNIEnv *env, jclass

clazz,jmethodID methodID, ...); jobject NewObjectA(JNIEnv *env, jclass

clazz,jmethodID methodID, jvalue *args);jobject NewObjectV(JNIEnv *env, jclass

clazz,jmethodID methodID, va_list args);

The clazz argument is a reference to the Java class of which we want to create an instance object. It cannot be an array class, which has its own set of JNI functions.

methodID is the constructor method ID, which can be obtained using the GetMethodID JNI function.

Page 18: Android JNI

Manipulating Classes in JNIjclass FindClass(JNIEnv *env, const char

*name);

Majorly used method when writing JNI code. Must pass full path of class. If you have to call this method for finding the most used class in

JNI, it is better to Cache it.

Page 19: Android JNI

Accessing Java Static and Instance fileds in Native code jfieldID data type: jfieldID is a regular C pointer pointing to a data

structure with details hidden from developers. We should not confuse it with jobject or its subtypes. jobject is a reference type corresponding to Object in Java, while jfieldID doesn't have such a corresponding type in Java. However, JNI provides functions to convert the java.lang.reflect.Field instance to jfieldID and vice versa.

Field descriptor: It refers to the modified UTF-8 string used to represent the field data type. The following table summarizes the Java field types and its corresponding field descriptors:

Page 20: Android JNI
Page 21: Android JNI

Accessing static fields

JNI provides three functions to access static fields of a Java class. They have the following prototypes:

jfieldID GetStaticFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig);

<NativeType> GetStatic<Type>Field(JNIEnv *env,jclass clazz, jfieldID fieldID);

void SetStatic<Type>Field(JNIEnv *env, jclass clazz, jfieldID fieldID,<NativeType> value);

Page 22: Android JNI

Accessing instance field

Accessing instance fields is similar to accessing static fields. JNI also provides the following three functions for us:

jfieldID GetFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig);

<NativeType> Get<Type>Field(JNIEnv *env,jobject obj, jfieldID fieldID);

void Set<Type>Field(JNIEnv *env, jobject obj, jfieldID fieldID, <NativeType> value);

Page 23: Android JNI

Calling static and instance methods from the native code

jmethodID data type: Similar to jfieldID, jmethodID is a regular C pointer pointing to a data structure with details hidden from the developers. JNI provides functions to convert the java.lang.reflect.Method instance to jmethodID and vice versa.

Method descriptor: This is a modified UTF-8 string used to represent the input (input arguments) data types and output (return type) data type of the method. Method descriptors are formed by grouping all field descriptors of its input arguments inside a "()", and appending the field descriptor of the return type. If the return type is void, we should use "V". If there's no input arguments, we should simply use "()", followed by the field descriptor of the return type. For constructors, "V" should be used to represent the return type. The following table lists a few Java methods and their corresponding method descriptors:

Page 24: Android JNI
Page 25: Android JNI

Calling static methods: JNI provides four sets of functions for native code to call Java methods.

Their prototypes are as follows:

jmethodID GetStaticMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig);

<NativeType> CallStatic<Type>Method(JNIEnv *env, jclass clazz, jmethodID methodID, ...);

<NativeType> CallStatic<Type>MethodA(JNIEnv *env, jclass clazz, jmethodID methodID, jvalue *args);

<NativeType> CallStatic<Type>MethodV(JNIEnv *env, jclass clazz,jmethodID methodID, va_list args);

Page 26: Android JNI

Calling instance methods:

Calling instance methods from the native code is similar to calling static methods. JNI also provides four sets of functions as follows:

jmethodID GetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig);

<NativeType> Call<Type>Method(JNIEnv *env, jobject obj, jmethodID methodID, ...);

<NativeType> Call<Type>MethodA(JNIEnv *env,jobject obj, jmethodID methodID, jvalue *args);

<NativeType> Call<Type>MethodV(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);

Page 27: Android JNI

Checking errors and handling exceptions in JNI JNI functions can fail because of system constraint (for example,

lack of memory) or invalid arguments (for example, passing a native UTF-8 string when the function is expecting a UTF-16 string).

Check for errors and exceptions: Many JNI functions return a special value to indicate failure. For example, the FindClass function returns NULL to indicate it failed to load the class. Many other functions do not use the return value to signal failure; instead an exception is thrown.

jboolean ExceptionCheck(JNIEnv *env); jthrowable ExceptionOccurred(JNIEnv *env);

void ExceptionDescribe(JNIEnv *env);

Page 28: Android JNI

Throw exceptions in the native code:

JNI provides two functions to throw an exception from native code. They have the following prototypes:

jint Throw(JNIEnv *env, jthrowable obj); jint ThrowNew(JNIEnv *env, jclass clazz, const

char *message);

The first function accepts a reference to a jthrowable object and throws the exception, while the second function accepts a reference to an exception class. It will create an exception object of the clazz class with the message argument and throw it.

Page 29: Android JNI

Extended Error Check Using CheckJNI

Android also offers a mode called CheckJNI, where the JavaVM and JNIEnv function table pointers are switched to tables of functions that perform an extended series of checks before calling the standard implementation.

List of Errors that CheckJNI tool can catch: Arrays: attempting to allocate a negative-sized array. Bad pointers: passing a bad jarray/jclass/jobject/jstring to a JNI call, or

passing a NULL pointer to a JNI call with a non-nullable argument. Class names: passing anything but the “java/lang/String” style of class name

to a JNI call. Critical calls: making a JNI call between a “critical” get and its

corresponding release. Direct ByteBuffers: passing bad arguments to NewDirectByteBuffer. Exceptions: making a JNI call while there’s an exception pending. JNIEnv*s: using a JNIEnv* from the wrong thread. jfieldIDs: using a NULL jfieldID, or using a jfieldID to set a field to a value of

the wrong type (trying to assign a StringBuilder to a String field, say), or using a jfieldID for a static field to set an instance field or vice versa, or using a jfieldID from one class with instances of another class.

Page 30: Android JNI

Enabling CheckJNI Enable CheckJNI:

For Production Builds adb shell setprop debug.checkjni 1

For Engineering Builds: adb shell stop adb shell setprop dalvik.vm.checkjni true adb shell start

Page 31: Android JNI

Memory Issues Using Libc Debug Mode

adb shell setprop libc.debug.malloc 1 adb shell stop adb shell start

Supported libc debug mode values are 1: Perform leak detection. 5: Fill allocated memory to detect overruns. 10: Fill memory and add sentinel to detect overruns.

Page 32: Android JNI

Further Reading Native Threads usage More about references JNI Graphics using OpenGL Audio using OpenSL apis. http://developer.android.com/training/articles/perf-jni.html

Page 33: Android JNI

Questions??