首页 > 代码库 > JNI简易教程,windows and linux

JNI简易教程,windows and linux

关于各种原理,jni和jna对比什么的请自行百度,这里仅介绍一下基本使用方法,方便初学者,也给自己留一份笔记而已。

1~3介绍jni的使用流程,4示例了java中string和byte[]与c++的类型转换,5简单介绍了native方法定义为static的相关问题

简单说一下jni的使用流程就像一个圆,从java出发,声明你需要的native方法,生成一个对应的.h文件,根据这个h文件构造c/c++工程生成dll/so动态库,最后返回来给java调用。

 首先要注意的是,要分开windows,linux,x32,x64。我是没有去捣鼓跨平台做这个jni,在什么平台用就统一用什么平台开发吧,linux上的eclipse-java工程就在linux上做c++工程生成.so动态库,windows上的就在vs上做c++工程生成.dll动态库。

1:

废话不多说,无论是windows还是linux,打开你的eclipse建一个java project,我习惯这样的目录结构

技术分享

LibTest.java:

1 package jni_lib;
2 
3 public class LibTest {
4     static {
5         System.loadLibrary("jni_lib_LibTest");
6     }
7 
8     public native static void HelloWorld(String i_instr);
9 }

JniTest.java:

1 package test;
2 
3 import jni_lib.LibTest;
4 
5 public class JniTest {
6     public static void main(String[] args) {
7         LibTest.HelloWorld(new String("I‘m Nycko"));
8     }
9 }

 

到这里java的代码可以说是全部写完了,当main方法执行时,将调用LibTest的HelloWorld方法,该方法的实现来自于jni_lib_LibTest.dll(libjni_lib_LibTest.so)中,那么这个工程现在缺少的就是这个动态库了,下面来生成这个动态库。

 

2:

无论是windows还是linux,你需要确保命令行可以运行javac,不行的自行百度安装jdk并且设置环境变量。下面以windows演示,linux的javac和javah命令用法一致,列出目录windows用dir,linux用ls

在定义native的.java文件目录下用javac编译该文件获得.class文件。

技术分享

然后前往该项目的bin目录,用javah处理刚才生成的.class文件,这里javah后面跟的不是路径,而是java中的包名+类名。

就会生成一个.h文件,该文件官方建议不要修改哦~,我们根据这个.h文件构建一个c/c++工程生成对应的动态库。

技术分享

jni_lib_LibTest.h:

 1 /* DO NOT EDIT THIS FILE - it is machine generated */
 2 #include <jni.h>
 3 /* Header for class jni_lib_LibTest */
 4 
 5 #ifndef _Included_jni_lib_LibTest
 6 #define _Included_jni_lib_LibTest
 7 #ifdef __cplusplus
 8 extern "C" {
 9 #endif
10 /*
11  * Class:     jni_lib_LibTest
12  * Method:    HelloWorld
13  * Signature: (Ljava/lang/String;)V
14  */
15 JNIEXPORT void JNICALL Java_jni_1lib_LibTest_HelloWorld
16   (JNIEnv *, jclass, jstring);
17 
18 #ifdef __cplusplus
19 }
20 #endif
21 #endif

 

3:

(windows)在visual studio中新建项目,选择visual c++的win32控制台应用程序,点击下一步然后应用程序类型改选为DLL,完成。

(linux)linux下新建一个工程不就是新建一个目录这么简单吗,哈哈,新建一个jni_lib_LibTest.cpp,不一定这个名字啦。

把步骤2中的.h文件拷贝到项目中,然后编写c++文件

jni_lib_LibTest.cpp:函数名从.h文件中拷贝过来,然后参数类型后面需要添加你的变量名。像jstring这样的类型从java传过来无法直接当作c++的string使用,大部分变量都需要jni的方法做转换,这里先介绍jni的工序,更详细的代码实现先忽略。

// jni_lib_LibTest.cpp : 定义 DLL 应用程序的导出函数。
//

#include "stdafx.h"
//linux不要加这个文件
#include "jni_lib_LibTest.h" #include <iostream> using namespace std; /* * Class: jni_lib_LibTest * Method: HelloWorld * Signature: (Ljava/lang/String;)V */ JNIEXPORT void JNICALL Java_jni_1lib_LibTest_HelloWorld (JNIEnv *env, jclass, jstring j_i_inStr) { char* i_inStr; i_inStr = const_cast<char*>(env->GetStringUTFChars(j_i_inStr, false)); if (i_inStr == NULL) { cout << "i_inStr == NULl!" << endl; return ; /* OutOfMemoryError already thrown */ } cout << "OutPut in jni_lib_LibTest.cpp!" << endl; cout << "Helloworld! i_inStr is : " << i_inStr << endl; env->ReleaseStringUTFChars(j_i_inStr, i_inStr); }

(windows)然后就可以点击(生成->生成解决方案),生成相应的.dll动态库了,编译不通过的话看一下错误是什么咯,有可能遇到的问题是工程的SDK,include路径等等的属性设置问题,我使用了SDK=10.0.14393.0,附加包含目录=%JAVA_HOME%\include;%JAVA_HOME%\include\win32;%(AdditionalIncludeDirectories)。具体问题具体分析,自行解决哦~。另外要注意的是32和64位的问题,64位的java工程当然对应64位的动态库咯,我的机器是64的,java工程默认建出来是64所以这里vs生成的动态库选择x64来编译。

(linux)一句命令即可,简单

技术分享

 

然后把这个.dll(.so)动态库放到java工程目录下,自定义你的目录结构吧。我在根目录下建了一个jnilib文件夹放dll(.so)文件。

如果现在运行java,还是报错,真心塞,原来还要修改一下配置,如图添加以下路径即可。

技术分享

展开JRE System Library,双击 Native library location,选择你存放动态库的目录

技术分享

然后就可以运行你的java程序了,还不行的话要检查一下windows下System.loadLibrary只需要写文件名前面,后缀.dll不需要写,linux下更是lib和.so都不要,只取中间的动态库名字即可。

输出如下:

技术分享

 

4:介绍两个常用的类型转换

string获取: 

 获取传入参数String:jstring java_string,其中const_cast<char*>是为了去掉const属性
    char* str_cpp; 
    str_cpp = const_cast<char*>(env->GetStringUTFChars(java_string, false)); 
    if(str_cpp == NULL) { 
       return NULL; /* OutOfMemoryError already thrown */ 
    }

 //do something

    env->ReleaseStringUTFChars(java_string, str_cpp);

srting返回:

     char* str;
     //do something
     return env->NewStringUTF(str);  

byte[]获取:

获取jbyteArray java_byte
     int byte_len= env->GetArrayLength(java_byte );
     BYTE* byte_value=http://www.mamicode.com/(BYTE*)(env->GetByteArrayElements(java_byte ,0));

byte[]返回:

     int byte_len = 10;
     BYTE* byte_value=http://www.mamicode.com/new BYTE[10]();
     jbyteArray jarrRV = env->NewByteArray(byte_len);
     env->SetByteArrayRegion(jarrRV, 0, byte_len , (jbyte*)byte_value);
     return jarrRV;

 

5:关于java中native方法是否定义为static的问题:

首先:定义为static的方法不需要创建该类的实例就能调用,对应生成的.h文件第二个参数是jclass类型,由于不是实例,所以估计(java基础不足)如果要在c++上使用这个参数也只能调用到同是static的其他成员变量或者方法。然后:非static方法需要先创建该类的实例才可以通过该实例调用,对应.h文件是第二个参数是jobject类型,是调用该方法的实例对象,可以用该变量在c++获取java该对象的其他成员。所以,如果修改了java工程中相关定义,记得要根据新生成的.h文件来修改.c/.cpp文件,这个类型一定要对上!

 

JNI简易教程,windows and linux