一般要使用 JNI 技术来实现 Java 应用程序和原生代码的通讯。java
任何使用JNI的操做都须要两次或者三次函数调用,所以要实现大量的原生方法并让它们同Java类保持同步很容易编程一件很是艰辛的工做。编程
而利用一些开源的方案则能够帮助咱们基于现有的原生代码接口自动生成 JNI 的代码。数组
在Java代码中使用native关键字能够声明原生方法,例如:多线程
public native String stringFromJNI();app
注意只是声明而已,“()”这对括弧后面直接就是分号了。函数
static { System.loadLibrary("hello-jni");//libhello-jni.so }学习
之因此要在静态代码块中调用 System.loadLibrary ,就是为了Java类首次加载和初始化时就能让原生代码的实现被载入。spa
原生代码是C/C++的,所以要有一个头文件明确要实现那些方法,JDK为咱们提供了javah(头文件生成器)来根据已经编译好的class生成头文件,例如:线程
javah –classpath bin/classes com.example.hellojni.HelloJni指针
javah 能够配置到 Eclipse 的 External Tools 中,方便使用。
Java代码中对原生方法的声明能够不带上参数,但对应的原生函数式带有参数的,例如:
JNIEXPORT jstring JNICALL Java_com_example_hellojni_HelloJni_stringFromJNI (JNIEnv *, jobject);
JNIEnv 是一个指针,指向可供JNI使用的函数列表。JNIEnv所指向的类型根据语言不一样(C/C++)而不一样,若是是C,则其所指向的类型就是 JNINativeInterface 结构,若是是C++,则是一个拥有成员方法的类实例。
若是要返回一个 String ,C的写法是:
return (*env)->NewStringUTF(env, "Hello from JNI !");//由于C中的JNI函数不清楚当前的JNI环境,因此要传入env。
而C++的写法则是:
return env->NewStringUTF("Hello from JNI !");
jobject 是一个引用了 HelloJni 类的实例的 Java 对象。
原生实例方法的定义示例以下:
JNIEXPORT jstring JNICALL Java_com_example_hellojni_HelloJni_stringFromJNI (JNIEnv * env, jobject thiz);
原生静态方法的定义示例以下:
JNIEXPORT jstring JNICALL Java_com_example_hellojni_HelloJni_stringFromJNI (JNIEnv * env, jclass clazz);
固然是分为了原生类型和引用类型。
原生类型在Java , JNI 和 C/C++中有各自的对应的表示方法,引用类型也是。
新建字符串:
jstring javaString; javaString = (*env)->NewStringUTF(env, "Hello World!");
Java 字符串转成 C 字符串: const jbyte* str; jboolean isCopy; str = (*env)->GetStringUTFChars(env, javaString, &isCopy); if (0 != str) { printf("Java string: %s", str); if (JNI_TRUE == isCopy) { printf("C string is a copy of the Java string."); } else { printf("C string points to actual string."); }
新建数组:
jintArray javaArray; javaArray = (env)->NewIntArray(env, 10); if (0 != javaArray) { / You can now use the array. */ }
经过操做副原本访问数组元素:
jint nativeArray[10]; (*env)->GetIntArrayRegion(env, javaArray, 0, 10, nativeArray);
将在 C 数组上的修改提交到 Java Array 中:
(*env)->SetIntArrayRegion(env, javaArray, 0, 10, nativeArray);
直接在指针上对数组进行操做:
jint* nativeDirectArray; jboolean isCopy; nativeDirectArray = (*env)->GetIntArrayElements(env, javaArray, &isCopy);
新建:
unsigned char* buffer = (unsigned char*) malloc(1024); ... jobject directBuffer; directBuffer = (*env)->NewDirectByteBuffer(env, buffer, 1024);
获取:
unsigned char* buffer; buffer = (unsigned char*) (*env)->GetDirectBufferAddress(env, directBuffer);
public class JavaClass { // Instance field private String instanceField = "Instance Field"; // Static field private static String staticField = "Static Field"; ... }
获取域ID:
jclass clazz; clazz = (*env)->GetObjectClass(env, instance);
jfieldID instanceFieldId; instanceFieldId = (*env)->GetFieldID(env, clazz, "instanceField", "Ljava/lang/String;");
获取静态域ID:
jstring staticField; staticField = (*env)->GetStaticObjectField(env, clazz, staticFieldId);
public class JavaClass { //Instance method. private String instanceMethod() { return "Instance Method"; } //Static method. private static String staticMethod() { return "Static Method"; } ... }
获取方法ID:
jmethodID instanceMethodId; instanceMethodId = (*env)->GetMethodID(env, clazz, "instanceMethod", "()Ljava/lang/String;");
获取静态方法ID:
jmethodID staticMethodId; staticMethodId = (*env)->GetStaticMethodID(env, clazz, "staticMethod", "()Ljava/lang/String;");
调用方法:
jstring instanceMethodResult; instanceMethodResult = (*env)->CallStringMethod(env, instance, instanceMethodId);
调用静态方法:
jstring staticMethodResult; staticMethodResult = (*env)->CallStaticStringMethod(env, clazz, staticMethodId);
Java Type Signature Boolean Z Byte B Char C Short S Int I Long J Float F Double D fully-qualified-class Lfully-qualified-class; type[] [type method type (arg-type)ret-type
使用 javap 能够从 class 文件中提取出域和方法的描述符:
javap –classpath bin/classes –p –s com.example.hellojni.HelloJni
public class JavaClass { //Throwing method. private void throwingMethod() throws NullPointerException { throw new NullPointerException("Null pointer"); } //Access methods native method. private native void accessMethods(); }
原生代码是这么写的:
jthrowable ex; ... (*env)->CallVoidMethod(env, instance, throwingMethodId); ex = (*env)->ExceptionOccurred(env); if (0 != ex) { (*env)->ExceptionClear(env); //Exception handler. }
jclass clazz; ... clazz = (*env)->FindClass(env, "java/lang/NullPointerException"); if (0 ! = clazz) { (*env)->ThrowNew(env, clazz, "Exception message."); }
jclass clazz; clazz = (*env)->FindClass(env, "java/lang/String");
新增全局引用:
jclass localClazz; jclass globalClazz; ... localClazz = (*env)->FindClass(env, "java/lang/String"); globalClazz = (*env)->NewGlobalRef(env, localClazz); ... (*env)->DeleteLocalRef(env, localClazz);
删除全局引用:
(*env)->DeleteGlobalRef(env, globalClazz);
新增全局弱引用:
jclass weakGlobalClazz; weakGlobalClazz = (*env)->NewWeakGlobalRef(env, localClazz);
验证全局弱引用是否可用:
if (JNI_FALSE == (*env)->IsSameObject(env, weakGlobalClazz, NULL)) { //Object is still live and can be used. } else { //Object is garbage collected and cannot be used. }
删除全局弱引用:
(*env)->DeleteWeakGlobalRef(env, weakGlobalClazz);
Java代码是这样写的:
synchronized(obj) { //Synchronized thread-safe code block. }
原生代码应该这样写:
if (JNI_OK == (*env)->MonitorEnter(env, obj)) { //Error handling. } //Synchronized thread-safe code block. if (JNI_OK == (*env)->MonitorExit(env, obj)) { // Error handling. }
将当前线程同虚拟机绑定和解绑: JavaVM* cachedJvm; ... JNIEnv* env; ... //Attach the current thread to virtual machine. (*cachedJvm)->AttachCurrentThread(cachedJvm, &env, NULL); //Thread can communicate with the Java application using the JNIEnv interface. //Detach the current thread from virtual machine. (*cachedJvm)->DetachCurrentThread(cachedJvm);