对JNI的概念一直不是很清楚,这几天断断续续花了点时间在这上面,在此做个总结,备忘。
JNI中很重要的一个概念是它的接口指针,该接口指针管理着对jvm内部函数的调用。JNI接口指针是一个指向指针数组的指针,指针数组中的元素指向了接口函数,通过这些接口函数可以实现java和c/c++或者说本地代码的交互。
写一个helloworld记录和分析大概的过程:
package com.demo;
public class HelloWorld {
private static int age;
private String name;
private static native void sayHello(String name);
private native String getName();
public static void main(String[] args){
HelloWorld hw = new HelloWorld();
hw.name = hw.getName();
sayHello(hw.name);
System.out.println(",Your age is: " + age);
System.out.println("Done!");
}
static {
System.loadLibrary("helloworld");
}
}
在当前目录下编译:
javac -d . HelloWorld.java
执行javah生成头文件:
javah -jni com.demo.HelloWorld
编辑com_demo_HelloWorld.c,用不同的方式修改和访问java类中定义的各个属性和方法
#include "com_demo_HelloWorld.h"
#include <stdio.h>
#include <stdlib.h>
/*
* Class: com_demo_HelloWorld
* Method: sayHello
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_com_demo_HelloWorld_sayHello
(JNIEnv *env, jclass cls, jstring name){
const jbyte *str;
str = (*env)->GetStringUTFChars(env, name, NULL);
if(str == NULL) return;
printf("Hello %s", str);
fflush(stdout); //让缓存中的数据及时显示到屏幕上
}
/*
* Class: com_demo_HelloWorld
* Method: getName
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_demo_HelloWorld_getName
(JNIEnv *env, jobject thiz){
char name[20];
jclass cls = (*env)->GetObjectClass(env, thiz);
jfieldID fid = (*env)->GetStaticFieldID(env, cls, "age", "I");
if(fid == NULL) return NULL;
(*env)->SetStaticIntField(env, cls, fid, 24);
printf("Enter your name:\n");
scanf("%s", name);
return (*env)->NewStringUTF(env, name);
}
生成so库的命令为:
gcc -I /usr/lib/jvm/java-6-openjdk/include/linux/ -I /usr/lib/jvm/java-6-openjdk/include/ -shared -o libhelloworld.so -fpic com_demo_HelloWorld.c
在当前目录有了libhelloworld.so文件,
在这个过程中,发现c文件中的prinf语句会滞后于System.out.println调用,先打印出了", Your age is: 24",而不是"Hello XXX",后来想起来i/o缓存的机制,添加了fflush(stdout),让printf函数及时把内容显示到终端上,程序运行的结果终于正确了。
JNI中还有其他很多内容,运用好JINEnv *env这个接口指针是关键!(等学深点再补充相应的内容)