jni开发编译

1.编写jni的代码,比如test.java,如下 

注意:System.loadLibrary函数的参数是动态库名,而不是动态库的函数名


package ttt;
import java.io.*;


public class test
{
        static
        {
            System.loadLibrary("test");  //对应libtest.so,注意这里不能使用libtest.so,只需要写lib跟.so之间的内容
         }
       public  static native byte[] getAll();                               //jni对外提供的调用接口
       public  static native byte[] getSingle(int i);


       public static void main(String[] args) {
           System.out.println(new String(getAll()));
           System.out.println(new String(getSingle(2)));
       }
}

2.运行javac test.java,生成test.class

3.生成头文件

因为test.java代码放在ttt目录下,到ttt目录外,执行javah ttt/test或javah -classpath . ttt,以便生成.h,这里生成的.h文件名为jni_ttt.h,该文件的内容为:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class jni_test */


#ifndef _Included_jni_test 
#define _Included_jni_test 
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     jni_test 
 * Method:    getAll
 * Signature: ()[B
 */
JNIEXPORT jbyteArray JNICALL Java_jni_test_getAll
  (JNIEnv *, jclass);


/*
 * Class:    jni_test 
 * Method:    getSingle
 * Signature: (I)[B
 */
JNIEXPORT jbyteArray JNICALL Java_jni_test_getSingle
  (JNIEnv *, jclass, jint);


#ifdef __cplusplus
}
#endif
#endif
其中,Java_jni_test_getAll、Java_jni_test_getSingle为接口函数,注意其返回值为jbyteArray。利用这个.h文件,编写一个.c或.cpp文件,实现相应的接口函数


4. 编写c或者cpp文件,其中必须包含刚才生成的.h头文件

注意,接口函数的参数都包含一个JNIEnv的上下文指针,通过这个上下文指针,可以调用jni库中的函数进行操作,

比如将char* 转换为 jbyteArray的代码如下:

JNIEXPORT jbyteArray JNICALL Java_jni_test_getSing(JNIEnv *env, jclass jc, jint i)
   ....
    char *strBuf = NULL;
    strBuf = malloc(100);
    strncpy(strBuf , input, 99);
    jbyteArray byteBuf = env->NewByteArray(strlen(strBuf ));                          //创建一个java的byte数组
    env->SetByteArrayRegion(byteBuf , 0, strlen(strBuf), (jbyte*)strBuf );
    if(strBuf) {
        free(strBuf);
    }
    ...
}

 另外要注意,对于c和c++,jni函数的参数是不同的,在c中NewByteArray函数的参数还要包含一个JNIEnv的指针,比如env->NewByteArray(env, strlen(strBuf )),否则,编译会报错:

error: request for member NewByteArray in something not a structure or union

error: request for member SetByteArrayRegion in something not a structure or union

对c++来说就相对简单一些

编译这个c或cpp代码使用:

gcc -fPIC -D_REENTRANT -I/usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/include/ -I/usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/include/linux -c jni_test.c  (使用c方式)

g++ -fPIC -D_REENTRANT -I/usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/include/ -I/usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/include/linux -c jni_test.c  (使用c++方式)

其中:

(/usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/include/和/usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/include/linux 为c、c++程序要用到的jni.h头文件所在的路径)

g++ -shared -Wl,-soname,libtest.so.1 -o libtest.so jni_test.o 

生成libtest.so


5. 将libtest.so拷贝到/usr/lib下,执行ldconfig,就可以让jni程序找到.so库,也可以在启动java程序的时候使用-Djava.library.path=指定so库所在路径

6. 此时,执行 java ttt/test命令,可能会提示:

Exception in thread "main" java.lang.NoClassDefFoundError: ttt/test
Caused by: java.lang.ClassNotFoundException: ttt.test.class
at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:247)

原因是classpath没有当前路径。改正后执行如下命令:

java -cp . ttt/test

7.将其打成jar包

jar -cef ttt.test test.jar ttt

c代表生成新的jar包;
e代表可执行的类,亦即main方法所在的类。书写时要加上包名,在本例中是后面的ttt.test;
f代表生成的jar包的名称,在本例中是test.jar。此包名可以随意命名,没有规定;
最后面的这个参数表示将ttt目录下的所有文件都打包放到新的jar包中。


参考:http://blog.csdn.net/xiaojianpitt/article/details/5652223

你可能感兴趣的:(jni开发编译)