Docs

Java Native Interface Specification—Contents

Java Native Interface: Programmer's Guide and Specification

(include more details and some usefule sample)

Jni Tips

http://developer.android.com/guide/practices/design/jni.html

Including:

Local and Global References

Threads

UTF-8 and UTF-16 Strings

Primitive Arrays

Exception

Native Libraries

xxx

Basicwhat's JNI

The JNI(Java Native Interface) is a native programming interface. It allows Java code that runs inside a Java Virtual Machine (VM) to interoperate with applications and libraries written in other programming languages, such as C, C++, and assembly. For Android, JNI can be used from 1.5 and later, and Android 2.3 introduces NativeActivity that enable you to implement an activity purely in native code.

why to  use JNI

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 such as assembly.

what can be done in JNI

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.

prototype of native method

Java__(

JNIEnv *env,        /* JNI interface pointer */

jobject obj,        /* "this" pointer */

jint i,             /* argument #1 */

jstring s           /* argument #2 */

)

Special comments:

a) The first argument - JNI interface pointer

The JNI interface is organized like a C++ virtual function table or a COM interface. An JNI interface pointer is a pointer to a pointer. This pointer points to an array of pointers, each of which points to an interface function. Every interface function is at a predefined offset inside the array. Figure at http://download.oracle.com/javase/6/docs/technotes/guides/jni/spec/images/designa.gif illustrates the organization of an interface pointer. The JNI interface pointer is only valid in the current thread. A native method, therefore, must not pass the interface pointer from one thread to another.

b) The second argument - "this" pointer:

this argument differs depending on whether the native method is static or nonstatic. For a nonstatic native method, it is a reference to the instance object of it Java class. For a static native method is a reference to its Java class.

c) Argument referencing style

Primitive types, such as integers, characters, and so on, are copied between Java and native code. Arbitrary Java objects, on the other hand, are passed by reference. The VM must keep track of all objects that have been passed to the native code, so that these objects are not freed by the garbage collector.

xxx

Develop procedure

1. Create a Java class that declares the native method.

Notes:

You need to completes the following tasks in the wrapper class:* Declare native interfaces exported from shared library

* Load the shared library implemented in C/C++ will

~/AndroidTest/JNITest$ cat com/mydomain/jprint.java

package com.mydomain;

public class jprint

{

public native void print();  Keyword 'native' means the method is exported from JNI library

static

{

//the following statement will load shared library libjnilibs.so on Linux (or libjnilibs.dll on Windodws). For Android, the library must be unpacked into /data/data//lib/libjnilibs.so at installation time by package manager。

System.loadLibrary("jnilibs");

}

}

2. Generate header file for native code

Compile the .java file to .class

~/AndroidTest/JNITest$ cd src/com/mydomain && javac jprint.java

or

~/AndroidTest/JNITest$ cd src/com/mydomain && javac -classpath /path/to/android.jar gen/path/to/R.java jprint.java

A file named "jprint.class" is generated.

Generate header file

~/AndroidTest/JNITest$ javah -jni jprint

A file named "com_mydomain_jprint.h" is generated,you can rename it to "jprint.h".

or

~/AndroidTest/JNITest$ cd src

~/AndroidTest/JNITest$ javah -jni com.xxx.xxx.JPrint

3. Implement all interfaces declared in the header files "jprint.h" with C/C++

gliethttp@Leith:~/Android$ cat hello.c

#include #include "jprint.h"

JNIEXPORT void JNICALL Java_jprint_print(JNIEnv *env, jobject obj)

{

printf("hello,Android!\n###Leith_gliethttp.\n");

}

5. Build native shared library

First, add the binary directory of Android NDK "ANDROID_NDK_PATH>/build/prebuilt/linux-x86/arm-eabi-4.2.1/bin" to environment

Then compile the native code as following:

~/AndroidTest/JNITest$ arm-eabi-gcc -fPIC -I/build/platforms/android-1.5/arch-arm/usr/include -nostdlib -Wl,-soname,libjnilibs.so -Wl,-Bsymbolic -Wl,--whole-archive -Wl,--no-whole-archive -Wl,--no-undefined -shared -o libjnilibs.so hello.c

for new ndk, you should use arm-linux-androideabi-gcc or arm-linux-androideabi-g++

Comments:

CFLAGS += -march=armv5te -mtune=xscale -msoft-float -fpic -mthumb-interwork -ffunction-sections -funwind-tables -fstack-protector -fno-short-enums -O2 -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__ -DANDROID

7. Implement a java code to call native interface via wrapper class "jprint"

gliethttp@Leith:~/Android$ cat gliethttp_hello.java

public class gliethttp_hello

{

public static void main(String[] args)

{

jprint p = new jprint();

p.print();

}

}

8, Build Java package

* Create libs/armeabi/ under project directory("libs" is under the same directory with directory "res","src");

* Copy native shared library libjnilibs.so to direcotry libs/armeabi/

* Press F5 to update files on Package Explorer Window of Eclipse

* Build project via Eclipse

Note: If you build JNI shared libraries for x86, you need to copy thoese libraries to libs/x86 rather than libs/armeabi

9. Run

* Install the installation package

* Launch the application

10, Tips

10.1 Use class type defined in Java application in C++ native code

* Get a class type according to class name

JNIEXPORT void JNICALL com_mydomain_utils_MyUtils(JNIEnv *env, jobject thisObj)

{

......

jclass cls = env->FindClass(thisObj, "com.mydomain.MyClass");

......

}

* Get a class type according to object

JNIEXPORT void JNICALL com_mydomain_utils_MyUtils(JNIEnv *env, jobject thisObj, jobject myObject)

{

......

jclass cls = env->GetObjectClass(myObject);

......

}

10.2 Transfer data from native code to Java via parameters

* In Java code

1) Define result class

package com.mydomain.utils;

public class MyResults

{

public int iData;

public MyResults()

{

}

public void setData(int iData)

{

this.iData = iData;

}

}

2) Call

MyResults retValue = new MyResults();

xxx->CallFunc(retValue); //Call native interface which will transfer data back to Java code via parameter

System.println("Data is = %d\r\n", retValue.iData);

* In native code

JNIEXPORT void JNICALL com_mydomain_utils_CallFunc(JNIEnv *env, jclass thisObj, jobject objRet)

{

jclass cls = env->GetObjectClass (objRet);

jmethodID mid = env->GetMethodID (cls, "setValue", "(I)V");

env->CallVoidMethod (objRet, mid, 20);  //call setValue of objRet which of type MyResults to set iData

}

11, Trouble Shooting11.1

Issue:

ERROR: failed to link libnativehelper.so

Solution:

A) Maybe more than one copies of library files exist, please check /data/data//lib and /system/lib to confirm that.

B) Relaunch the emulator, or flash correct fireware(Fireware verions is not compatible).

11.2

Issue:

ERROR: could not load "libxxx.so"

Issue:

A) The library file format is not arm eabi elf, in other words, you didn't build the library with arm-eabi-gcc;

B) You forgot to install the native shared library;

C) A library with the name of your library file exists in the system, such as libutils.so, but the original library doesn't export the interfaces you called in application.

11.3

For cracked device, or emulator, except package library files, you can transmit them to /system/lib directly:

$ adb remount

$ adb push libxxx.so /system/lib

11.4 Depended library

If liba.so is loaded in Java App, and liba.so depends on libb.so, but libb.so must be installed to /system/lib, otherwise, liba.so can't be loaded in java application, there are two solutions:

* Copy libb.so to /system/lib/ manually.

* Install liba.so and libb.so to /data/data//lib/, but invoke interfaces exported in libb.so using dlopen/dlclose/dlsym.

11.5

Error

App can't run, it crushed at loading libraries

Solution:

1) The library file format is not arm eabi elf, in other words, you didn't build the library with arm-eabi-gcc;

11.6

Error:

Application crushes when calls a interface via jni

Solution:

1) The library doesn't export the interface, or the interface is not implemented, or the interface implementation contains critical error.

12. There is sample in Android NKD, just run "make -f GNUMakefile APP=hello-jni V=1" to build the sample.

Data Type

In native library, in order to pass some arguments to Java layer, or get value from Java layer, you should use native type(such as jint, jlong, jbyte) rather than common c/c++ type (such as int, long, char). for example, to invoke the method "int read(byte[] buf, int offset, int len)" in native library:

char buf[100];

jmethodID midRead;

jobject obj;

jbyteArray bytes = env->NewByteArray(sizeof(buf));

jint len = env->CallIntMethod(obj, midRead, bytes, 0, (jint)(sizeof(buf))); //cast the type of sizeof(buf) to jint, and return type should be jint

if (len > 0)

{

env->GetByteArrayRegion(bytes, 0, len, (jbyte *)buf);

}

More data type, please refer to

JNI function

How to invoke JNI funtion in C/C++

=======================================

Suppose there is an JNI function

const char * GetStringUTFChars(JNIEnv *env, jstring string, jboolean *isCopy);

Invoked in C:

jstring s = xxxxxxxx;

const char *str = (*env)->GetStringUTFChars(env, s, 0);

(*env)->ReleaseStringUTFChars(env, s, str); //(*env)->A_JNI_FUN(env,...)

Invoked in C++:

jstring s = xxxxxxxx;

const char *str = env->GetStringUTFChars(s, 0);//env->A_JNI_FUN(...)

env->ReleaseStringUTFChars(s, str);

Usefule JNI functions

=======================================

Class Operations

----------------------------------

FindClass: loads a class with the spedified name.

GetSuperclass:

IsAssignableFrom: Determines whether an object of clazz1 can be safely cast to clazz2.

Global and Local References

----------------------------------

NewLocalRef: Creates a new local reference that refers to the same object as ref. GC will feed the obj automatically, you can also invoke DeleteLocalRef to release it explicitly

DeleteLocalRef:

NewGlobalRef: Creates a new global reference to the object referred to by the obj argument. The obj argument may be a global or local reference. Global references must be explicitly disposed of by calling DeleteGlobalRef().

DeleteGlobalRef:

NewWeakGlobalRef: Creates a new weak global reference

DeleteWeakGlobalRef:

PushLocalFrame: Creates a new local reference frame, in which at least a given number of local references can be created

PopLocalFrame: Pops off the current local reference frame, frees all the local references

Object Operations

----------------------------------

AllocObject: Allocates a new Java object without invoking any of the constructors for the object

NewObject/NewObjectA/NewObjectV: Constructs a new Java object. The method ID indicates which constructor method to invoke. This ID must be obtained by calling GetMethodID() with as the method name and void (V) as the return type. eg: cid = (*env)->GetMethodID(env, stringClass, "", "()V");  (*env)->NewObject(env, stringClass, cid, elemArr);

GetObjectClass: Returns the class of an object.

IsInstanceOf: Tests whether an object is an instance of a class.

IsSameObject: Tests whether two references refer to the same Java object.

Accessing Fields of Objects

----------------------------------

1, Object fields

GetFieldID:

GetField: Returns the value of an instance (nonstatic) field of an object. The field to access is specified by a field ID obtained by calling GetFieldID().

SetField: Sets the value of an instance (nonstatic) field of an object. The field to access is specified by a field ID obtained by calling GetFieldID().

2, Static fields

GetStaticFieldID:

GetStaticField:

SetStaticField:

Calling Instance Methods

----------------------------------

1, Object fields

GetMethodID: Returns the method ID for an instance (nonstatic) method of a class or interface. To obtain the method ID of a constructor, supply as the method name and void (V) as the return type.

CallMethod/CallMethodA/CallMethodV: Call a Java instance method from a native method

CallNonvirtualMethod/CallNonvirtualMethodA/CallNonvirtualMethodV:invoke an instance (nonstatic) method on a Java object, according to the specified class and method ID

2, Static fields

GetStaticMethodID

CallStaticMethod/CallStaticMethodA/CallStaticMethodV

String Operations

----------------------------------

1, UNICODE characters:

NewString: Constructs a new java.lang.String object from an array of Unicode characters.

GetStringLength: Returns the length (the count of Unicode characters) of a Java string.

GetStringRegion: Copies len number of Unicode characters beginning at offset start to the given buffer buf.

GetStringChars: Returns a pointer to the array of Unicode characters of the string, This pointer is valid until ReleaseStringchars() is called.

ReleaseStringChars:

2, UTF-8 characters

NewStringUTF: Constructs a new java.lang.String object from an array of characters in modified UTF-8 encoding.

GetStringUTFLength:

GetStringUTFChars:

ReleaseStringUTFChars:

GetStringUTFRegion:

Array Operations

----------------------------------

GetArrayLength:

NewObjectArray:

GetObjectArrayElement: Returns an element of an Object array.

SetObjectArrayElement: Sets an element of an Object array.

NewArray: Construct a new primitive array object, ex: NewBooleanArray/NewShortArray

GetArrayElements: Return the body of the primitive array, the result is valid until the corresponding ReleaseArrayElements() function is called

ReleaseArrayElements: Informs the VM that the native code no longer needs access to

GetArrayRegion: Copies a region of a primitive array (Java array, such as int[] ) into a buffer (Native buffer, such int *).

SetArrayRegion: Copies back a region of a primitive array from a buffer.

Exception

----------------------------------

Throw: Causes a java.lang.Throwable object to be thrown.

ThrowNew: Constructs an exception object from the specified class with the message specified by message and causes that exception to be thrown.

How to process if JNI library depends on other libraries

Scenario 1

libjni.so -> liba.so -> libb.a

- Link libb.a when to build liba.so (append -lb to LDFLAG)

- Link liba.so when to build libjni.so (append -la to LDFLAG)

- Copy liba.so and libjni.so to libs/armeabi/

- Load libraries in .java in the following order

static

{

System.loadLibrary("a");  //Must load liba.so before libjni.so

System.loadLibrary("jni");

}

Scenario 2

libjni.so -> libx.so and liby.so

libx.so -> liby.so

liby.so -> libz.so

- Link libz.so when to build liby.so (append -lz to LDFLAG)

- Link liby.so when to build libx.so (append -ly to LDFLAG)

- Link libx.so and liby.so when to build libxjni.so (append -lx -ly to LDFLAG)

- Copy libx.so, liby.so, libz.so and libjni.so to libs/armeabi/

- Load libraries in .java in the following order

static

{

System.loadLibrary("z");

System.loadLibrary("y");

System.loadLibrary("x");

System.loadLibrary("jni");

}

The signature of type 'void' is 'V'

NewString/NewStrinUTF

Must check if the char/jchar argument is NULL, if so, don't call these APIs, since the return value is wrong (the return value is not NULL);

Refer to inner class in jni

Use the class name 'OuterClass$InnerClass' rether than 'OuterClass.InnerClass'.

Use exception

You can't use try-catch in jni, you must use exceptionXXX functions instead

Register Natives: Binde Java method with JNI function by yourself, that means the name of JNI function needs not to be Java_com_xxx_xxx_your_java_method_name

///

//In Jni code

static jint

add(JNIEnv *env, jobject thiz, jint a, jint b) {

int result = a + b;

LOGI("%d + %d = %d", a, b, result);

return result;

}

static const char *classPathName = "com/example/android/simplejni/Native";

static JNINativeMethod methods[] = {

{"add", "(II)I", (void*)add },

};

/*

* Register several native methods for one class.

*/

static int registerNativeMethods(JNIEnv* env, const char* className,

JNINativeMethod* gMethods, int numMethods)

{

jclass clazz;

clazz = env->FindClass(className);

if (clazz == NULL) {

LOGE("Native registration unable to find class '%s'", className);

return JNI_FALSE;

}

if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {

LOGE("RegisterNatives failed for '%s'", className);

return JNI_FALSE;

}

return JNI_TRUE;

}

/*

* Register native methods for all classes we know about.

*

* returns JNI_TRUE on success.

*/

static int registerNatives(JNIEnv* env)

{

if (!registerNativeMethods(env, classPathName,

methods, sizeof(methods) / sizeof(methods[0]))) {

return JNI_FALSE;

}

return JNI_TRUE;

}

/

//in Java Code

package com.example.android.simplejni;

import android.app.Activity;

import android.os.Bundle;

import android.widget.TextView;

public class SimpleJNI extends Activity {

/** Called when the activity is first created. */

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

TextView tv = new TextView(this);

int sum = Native.add(2, 3);

tv.setText("2 + 3 = " + Integer.toString(sum));

setContentView(tv);

}

}

class Native {

static {

// The runtime will add "lib" on the front and ".o" on the end of

// the name supplied to loadLibrary.

System.loadLibrary("simplejni");

}

static native int add(int a, int b);

}

Build execubatle file

Link shared libraries

LDALGS += -nostdlib -Wl,-dynamic-linker,/system/bin/linker \ /build/prebuilt/linux-x86/arm-eabi-4.2.1/arm-eabi/lib/ldscripts/armelf.x \             /build/platforms/android-1.5/arch-arm/usr/lib/crtbegin_dynamic.o \             /build/platforms/android-1.5/arch-arm/usr/lib/crtend_android.o

LIBS += -lc -ldl

Link static libraries

LDALGS += -nostdlib -static  \

/build/prebuilt/linux-x86/arm-eabi-4.2.1/arm-eabi/lib/ldscripts/armelf.x \

/build/platforms/android-1.5/arch-arm/usr/lib/crtbegin_dynamic.o \

/build/platforms/android-1.5/arch-arm/usr/lib/crtend_android.o

LIBS += -lc -lgcc

Tips

When to build .cpp code with XXX-g++

If you will build shared library for NDK r4 and previous version, or for compatibility reason with previous version,  please add CXXFLAGS: -fno-exceptions-fno-rtti

Build for Atom

GCC=i686-android-linux-gcc

GXX=i686-android-linux-g++

CFLAGS +=-march=i686 -msse3 -mstackrealign -mfpmath=sse --sysroot=$(ANDROID_NDK_ROOT)

LIBS += crtbegin_so.o

xxx

xxx

Build issue & solution:

error:

undefined reference to `__cxa_end_cleanup'undefined reference to `__gxx_personality_v0'

Solution:

Add compile flags -fno-exceptions

error:

undefined reference to 'vtable for __cxxabiv1::__class_type_info'

undefined reference to 'vtable for __cxxabiv1::__si_class_type_info'

Solution:

Add compile flags -fno-rtti

[Issue] crt0.o: No such file: No such file or directory collect2: ld returned 1 exit status

[Solution] add building flag "-nostdlib"

[Issue] warning: cannot find entry symbol _start; defaulting to....

[Solution] link crtbegin_dynamic.o or crtbegin_static.o; or ignore this warning. since if you add crtbegin_dynamic.o, the application may be dead-locked.

[Issue] when run binary file,error message "binary_file: not found

[Solution] add ldflags "-Wl,-dynamic-linker,/system/bin/linker" during building

[Issue] When main function returns (after executing 'return 0'), segmentation fault signal is triggered

[Solution 1st] add "exit(0);" just before "return 0;" in source code.

[Solution 2nd] add ldflags "/build/prebuilt/linux-x86/arm-eabi-4.2.1/arm-eabi/lib/ldscripts/armelf.x /build/platforms/android-1.5/arch-arm/usr/lib/crtbegin_dynamic.o /build/platforms/android-1.5/arch-arm/usr/lib/crtend_android.o" during building

[Issue]

undefined reference to __aeabi_unwind_cpp_pr1

[Solution]

Add libs -lgcc -lc -lgcc

Cannot load library: link_image[2023]: failed to link xxx.so

May be caused by one of the the following reason:

The library doesn't exist

the library name or path doesn't exist

You load library with System.loadLibrary without 'lib' prefix stripped. ie, for a lib libutils.so, you need to load it with System.loadLibrary("utils"), but neigher System.loadLibrary("libutils") nor System.loadLibrary("libutils.so");

The flags to build shared library is wrong:

'-fno-exception -fno-rtti' is not specified when to build .cpp for old NDK

xxx

xxx

Debug

How to debug

- Write log

- Call __android_log_print and other andoid log interfaces declared in android/log.h, the interface is exported in liblog.so.

xxx

Refs

Android Atom

Using the Android-x86 Port as your Emulator

http://androiddevnotes.com/2011/03/08/1299521520000.html

android-x86 (Atom)

Android adb连接VirtualBox方式

http://blog.sina.com.cn/s/blog_4de067e40100povf.html

xxx

xxx

...

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐