实践出真知,首先是从Hello JNI开始接触
目标文件包括:Hello.c Hello.class Hello.h Hello.java libHello.so;实现功能为在标准输出上打印Hello JNI
操作流程是:
- 编写Hello.java
- 用javac Hello.java 生成 Hello.class
- 用javah -jni Hello生成Hello.h
- 然后编写Hello.c,使用Gcc编译共享库libHello.so
- 最后调用java运行
共享库的编译命令为:gcc -shared -fPIC -I /usr/local/jdk1.6.0_30/include/ -I /usr/local/jdk1.6.0_30/include/linux/ -I /usr/local/ Hello.c -o libHello.so
运行命令为:java -Djava.library.path=. Hello
以下是主要文件代码
Hello.java

1 public class Hello{ 2 static { 3 System.loadLibrary("Hello"); 4 } 5 public native void println(); 6 public static void main(String args[]){ 7 (new Hello()).println(); 8 } 9 }
Hello.c

1 #include<stdio.h> 2 #include"Hello.h" 3 4 JNIEXPORT void JNICALL Java_Hello_println (JNIEnv *env, jobject obj) { 5 printf("Hello JNI\n"); 6 return; 7 }
1、 JNI的数据类型
基础数据类型(包括从8位、16位、32位到64位的8个(每种各两个)外加一个void,一共9个数据类型)、引用数据类型(jobject)、方法ID和域ID、值类型、UTF8字符串
需要注意的是Java的Char是双字节(这JNI的基础类型中也一样)而C和C++中的Char是单字节。
2、JNI接口函数命名方式
获取方法签名:javap -classpath <jar path> -s <class qualified name> (e.g. javap -classpath /usr/local/jdk1.6.0_30/jre/lib/rt.jar -s java.lang.String)
3、 JNI函数与API
详细查看 ${JAVA_HOME}/include/jni.h,定义的 struct JNIEnv 中包括了JNI的若干接口
4、修改Hello JNI
如果想在打印信息里面加入一些可变信息可以通过向JNI的方法中传参处理,那么需要对Hello.java和Hello.c做如下修改
Hello.java

1 package basic ; 2 public class Hello{ 3 private static final String name="xxx"; 4 static { 5 System.loadLibrary("Hello"); 6 } 7 public native void println(String name); 8 public static void main(String args[]){ 9 (new Hello()).println(name); 10 } 11 }
Hello.c

1 #include<stdio.h> 2 #include"Hello.h" 3 4 JNIEXPORT void JNICALL Java_basic_Hello_println (JNIEnv *env, jobject obj, jstring str) { 5 // get the object's class handle 6 jclass clsstr=(*env)->FindClass(env,"java/lang/String"); 7 // create a string object in UTF-8 format 8 jstring strencode=(*env)->NewStringUTF(env,"utf-8"); 9 // get the object's method handle, JNIenv, Object class, method name, method signature 10 // One can get the method signature by running "javap -s <class name>" 11 jmethodID mid=(*env)->GetMethodID(env,clsstr,"toCharArray","()[C"); 12 // invoke the object method, JNIenv, java object, method ID, parameters 13 jcharArray barr=(jcharArray)(*env)->CallObjectMethod(env,str,mid,strencode); 14 jsize alen=(*env)->GetArrayLength(env,barr); 15 16 jboolean tt=JNI_TRUE;// or JNI_FALSE 17 jboolean *t=&tt; 18 // get a point to the header of a char array, JNIenv, the array, is copy 19 jchar* ba=(*env)->GetCharArrayElements(env,barr,t); 20 21 while(*ba!='\0') 22 printf("%c",*(ba++)); 23 ba-=alen; 24 // release the reference of the array 25 (*env)->ReleaseCharArrayElements(env,barr,ba,0); 26 printf(" Hello JNI\n"); 27 return; 28 }
最后运行修改后的Hello命令:java -Djava.library.path=. basic.Hello
运行结果:xxx Hello JNI
5、JNI中获取方法和变量域
大概需要如下三个步骤:
获取对象的类,例如
jclass clsstr=(*env)->FindClass(env,"java/lang/String"); jclass cls=(*env)->GetObjectClass(env,myobj); 等等
获取类中函数/变量域的ID(GetMethodID,GetFieldID),例如
/* * 使用 javap -classpath <jar path> -s <class qualified name> 获取函数签名 */ jmethodID mid=(*env)->GetMethodID(env,clsstr,"toCharArray","()[C"); /* * JNI定义中有变量域的官方定义标签,JNI Data Structures */ jfieldID fid_msg=(*env)->GetFieldID(env,cls,"val","Z");
调用JNI中方法对函数/变量进行访问,例如
//变量访问 printf("%d\n",(*env)->GetBooleanField(env,myobj,fid_msg)); //方法调用 jcharArray barr=(jcharArray)(*env)->CallObjectMethod(env,str,mid,strencode);
Reference