-
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
-
Basic
-
what'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
-
...
阅读(5586) | 评论(0) | 转发(0) |