JNI学习1

实践出真知,首先是从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

View Code
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

View Code
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接口函数命名方式

一般JNI接口函数命名为:Java_包名_类名_方法名 (e.g. Java_basic_Hello_println)

获取方法签名: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

View Code
 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

View Code
 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

Java基础知识——JNI入门介绍(下)

JNI使用规范详解

posted @ 2013-04-10 15:20  asdffdas  阅读(204)  评论(0编辑  收藏  举报