上一篇写了一下java使用jni的入门,这篇写一下如何通过jni操作java对象。用eclipse开发的不一定会开发java,就像上篇例子那样,离开eclipse只能写个helloworld,如果写个大型的java工程可能就不行了。本文也顺带记录一下不用eclipse怎么组织一个java工程。
工程结构
这篇想手敲还挺费劲,由于命令较多,搞不好会有遗漏,还是写个makefile直接粘贴吧。
工程结构很简单,如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19/root/devel/java/test2
├── classes
│ ├── a
│ │ ├── b
│ │ │ ├── c
│ │ │ │ └── PersonJni.class
│ │ │ └── Person.class
│ │ └── Hello.class
│ ├── a_b_c_PersonJni.h
│ └── libperson.so
└── src
├── a
│ ├── b
│ │ ├── c
│ │ │ └── PersonJni.java
│ │ └── Person.java
│ └── Hello.java
├── makefile
└── PersonJniImpl.c
我这个组织结构不一定合理,我的原则是需要手敲的所谓代码都放到src
目录下,所有生成的东西都放到classes
目录下。
写makefile
1 | JDKHOME = /opt/jdk1.7.0_79 |
这里的makefile是为了方便写这篇文章组织的,没有按照规范写,以后再写makefile的东西吧。
写java代码
不多说了,直接贴代码
Person.java
1 | package a.b; |
PersonJni.java
1 | package a.b.c; |
Hello.java
1 | package a; |
写c代码
写c代码之前要先make
一下,这样能生成对应jni的头文件,然后就可以编写PersonJniImpl.c
了。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
JNIEXPORT void JNICALL Java_a_b_c_PersonJni_test
(JNIEnv *env, jobject obj)
{
printf("this is jni test.\n");
}
JNIEXPORT jstring JNICALL Java_a_b_c_PersonJni_getName
(JNIEnv *env, jobject o, jobject p)
{
jclass personClass = (*env)->GetObjectClass(env, p);
jmethodID getName = (*env)->GetMethodID(env, personClass, "getName", "()Ljava/lang/String;");
jstring name = (jstring)(*env)->CallObjectMethod(env, p, getName);
//const char *str = (*env)->GetStringUTFChars(env, name, 0);
//return (*env)->NewStringUTF(env, str);
return name;
}
JNIEXPORT jint JNICALL Java_a_b_c_PersonJni_getAge
(JNIEnv *env, jobject o, jobject p)
{
jclass personClass = (*env)->GetObjectClass(env, p);
jmethodID getAge = (*env)->GetMethodID(env, personClass, "getAge", "()I");
jint age = (*env)->CallIntMethod(env, p, getAge);
return age;
}
JNIEXPORT jstring JNICALL Java_a_b_c_PersonJni_getInfo
(JNIEnv *env, jobject o)
{
jclass personClass = (*env)->FindClass(env, "a/b/Person");
jmethodID getInfo = (*env)->GetStaticMethodID(env, personClass, "getInfo", "()Ljava/lang/String;");
jstring info = (jstring)(*env)->CallStaticObjectMethod(env, personClass, getInfo);
return info;
}
JNIEXPORT jobject JNICALL Java_a_b_c_PersonJni_constructPerson
(JNIEnv *env, jobject o, jstring name, jint age)
{
jclass personClass = (*env)->FindClass(env, "a/b/Person");
jmethodID personConstructMethod = (*env)->GetMethodID(env, personClass, "<init>", "(Ljava/lang/String;I)V");
jobject person = (*env)->NewObject(env, personClass, personConstructMethod, name, age);
return person;
}
JNIEXPORT void JNICALL Java_a_b_c_PersonJni_setNameAge
(JNIEnv *env, jobject o, jobject p, jstring name, jint age)
{
jclass personClass = (*env)->GetObjectClass(env, p);
jmethodID setName = (*env)->GetMethodID(env, personClass, "setName", "(Ljava/lang/String;)V");
(*env)->CallVoidMethod(env, p, setName, name);
jmethodID setAge = (*env)->GetMethodID(env, personClass, "setAge", "(I)V");
(*env)->CallVoidMethod(env, p, setAge, age);
}
这里需要强调一下,c和c++的jni实现的写法是不一样的。每个写一行对比一下吧。
c写法:1
(*env)->GetObjectClass(env, p);
c++写法:1
env->GetObjectClass(p);
只要牵涉env指针操作的都是这个规律
编译、测试
切换到makefile所在的目录,执行如下命令:1
2
3
4$ make
$ cd ../classes
$ export LD_LIBRARY_PATH=.
$ java a.Hello
测试结果:1
2
3
4
5
6
7
8Picked up _JAVA_OPTIONS: -Xmx2048m -XX:MaxPermSize=512m -Djava.awt.headless=true
hello, 小明:22
this is jni test.
小明
22
这是一个人
hello, 小红:20
hello, 小刚:33
咦,结果中第一行是什么鬼?其实它是因为我设了一个_JAVA_OPTIONS
的环境变量后出现的,因为我之前编译hadoop相关的的东西时,由于要求jvm内存很大,而默认的不能满足,所以设置了它。先不管,以后有时间再说个中含义吧。
附:打jar包
通过上面的操作可以知道怎么组织java工程了,但是,我们平时执行java程序时一般不会拿一堆class文件去执行,一般都会打成个jar包。下面记录一下把上面生成的类文件打成jar包并能正常执行输出测试结果的过程。
假设现在还在makefile文件所在的src目录,执行如下命令:1
2
3$ cd ../classes
$ tar cf hello.jar a #创建带自动生成的MANIFEST.MF文件的jar,这是执行测试是不行的,提示找不到主类
$ tar xf hello.jar #解压包
修改解压出来的META-INF/MANIFEST.MF,修改内容如下(记住最后有个空行):1
2
3Manifest-Version: 1.0
Created-By: 1.7.0_79 (Oracle Corporation)
Main-Class: a.Hello
前两行是打包时自动生成的,Main-Class: a.Hello
是修改时添上的,意在指定主类(即main函数入口类)。
再执行如下命令重新打包,并测试执行:1
2$ jar cvfm hello1.jar META-INF/MANIFEST.MF a #经过这样打包就能找到主类了
$ jar -jar hello1.jar #执行测试程序
经过以上操作,jar打包就完成了。