您的位置:首页 > 编程语言 > C语言/C++

Windows7 环境中借助jni实现Java 调用C++接口(一)

2018-03-23 18:51 393 查看
在软件开发过程中,常会出现Java语言需要调用C/C++语言接口的场景,例如:受JRE的限制,大部分系统底层接口均不能被Java程序直接调用,而C/C++则可以方便地调用这些接口,此时Java语言就可以通过调用C++接口,间接使用这些功能。本文以“获取用户按键”、“输出信息于控制台指定位置”两个接口,说明了Java程序借助Java本地方法(jni)调用C/C++接口的过程。

开发环境

VisualStudio 2013

jdk8以上

Windows7以上操作系统

步骤

1. Java侧定义jni接口

/*
* jniM.java
* Java native class
*/
class JniM {
// 接口调用成功测试
public native void sayRunPlace();

// 获取用户按键 (当前只识别方向键、a、d、w、s、空格共9个按键)
public native String getKeyCode();

// 在控制台(0,0)位置输出信息
public native void showPanel(String[][] args, int iHight, int iWide);
}


2. 生成jni对应的C语言头文件

2.1 编译jniM.java,并生成C语言头文件jniM.h文件

$javac jniM.java

$javah JniM


2.2 生成的C头文件如下:

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

#ifndef _Included_JniM
#define _Included_JniM
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class:     JniM
* Method:    sayRunPlace
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_JniM_sayRunPlace
(JNIEnv *, jobject);

/*
* Class:     JniM
* Method:    getKeyCode
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_JniM_getKeyCode
(JNIEnv *, jobject);

/*
* Class:     JniM
* Method:    showPanel
* Signature: ([[Ljava/lang/String;II)V
*/
JNIEXPORT void JNICALL Java_JniM_showPanel
(JNIEnv *, jobject, jobjectArray, jint, jint);

#ifdef __cplusplus
}
#endif
#endif


3. 生成动态库文件

3.1 在VS2013中建立win32控制台工程





3.2 加入步骤2中生成”jniM.h”文件,并建立“jniM.cpp”文件,加入工程:



3.3 在“jniM.cpp”中实现“jniM.h”中定义的方法,代码如下:

/*Cpp method archieve*/
#include "stdio.h"
#include <conio.h>
#include <string>
#include <windows.h>

#include "JniM.h"

// C++string转为jstring,如无需要,可不实现该函数
jstring CStr2Jstring(JNIEnv* env, const char* pat)
{
//定义java String类 strClass
jclass strClass = (env)->FindClass("Ljava/lang/String;");
//获取java String类方法String(byte[],String)的构造器,用于将本地byte[]数组转换为一个新String
jmethodID ctorID = (env)->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V");
//建立byte数组
jbyteArray bytes = (env)->NewByteArray((jsize)strlen(pat));
//将char* 转换为byte数组
(env)->SetByteArrayRegion(bytes, 0, (jsize)strlen(pat), (jbyte*)pat);
//设置String, 保存语言类型,用于byte数组转换至String时的参数
jstring encoding = (env)->NewStringUTF("GB2312");
//将byte数组转换为java String,并输出
return (jstring)(env)->NewObject(strClass, ctorID, bytes, encoding);
}

// Test Function
JNIEXPORT void JNICALL Java_JniM_sayRunPlace(JNIEnv *, jobject)
{
printf("I'm running in C++.\n\n");
}

// Get pushed key
JNIEXPORT jstring JNICALL Java_JniM_getKeyCode(JNIEnv *env, jobject)
{
unsigned int uiKeyScanCode = _getch();

//Direction key
if (0xE0 == uiKeyScanCode)
{
uiKeyScanCode = _getch() * 0x100 + uiKeyScanCode;
}
else if (0 == uiKeyScanCode)
{
std::string strTmp("");
return CStr2Jstring(env, strTmp.c_str());
}

std::string strKeyValue("");
switch (uiKeyScanCode){
case 0x4be0:
strKeyValue = "Left";
break;
case 0x4de0:
strKeyValue = "Right";
break;
case 0x50e0:
strKeyValue = "Down";
break;
case 0x48e0:
strKeyValue = "Up";
break;
case 0x0061:
strKeyValue = "a";
break;
case 0x0064:
strKeyValue = "d";
break;
case 0x0073:
strKeyValue = "s";
break;
case 0x0077:
strKeyValue = "w";
break;
case 0x0020:
strKeyValue = " ";
break;
default:
strKeyValue = "Invalid";
break;
}

return CStr2Jstring(env, strKeyValue.c_str());
}

// Show panel on console
JNIEXPORT void JNICALL Java_JniM_showPanel(JNIEnv *env, jobject, jobjectArray jObjAry, jint, jint)
{
system("cls");
COORD pos = { 0, 0 };    //设定坐标
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);    //函数句柄
SetConsoleCursorPosition(hOut, pos);

int iLen = env->GetArrayLength(jObjAry);
for (int iLoop = 0; iLoop < iLen; ++iLoop)
{
jarray jAry = (jarray)env->GetObjectArrayElement(jObjAry, iLoop);
int iLenSnd = env->GetArrayLength(jAry);
char* cStr = NULL;
for (int iLoopSnd = 0; iLoopSnd < iLenSnd; ++iLoopSnd)
{
jstring jStr = (jstring)env->GetObjectArrayElement((jobjectArray)jAry, iLoopSnd);

cStr = (char*)env->GetStringUTFChars(jStr, 0);
if ('0' == *cStr)
{
printf(" ");
}
else if ('1' == *cStr)
{
printf("■");
}
else
{
printf("%1s", cStr);
}
}
}
return;
}


3.4 配置VS2013编译选项

C++编译目录包含jdk头文件所在路径:



C++编译模式为Release,同时根据运行机操作系统选择生成64位还是32位动态库



3.5编译生成dll文件:





4. Java侧加载动态库,并调用本地方法:

拷贝VS生成的动态库(.dll)文件到加载Java工程目录。Java示例代码如下:

/*
* jniDemo.java
* Test class for Jni
*/

class JniDemo {
// 加载动态库
static {
System.loadLibrary("cppadp");
}

public static void main(String[] args) {
JniM oJniM = new JniM();
//Test Code
oJniM.sayRunPlace();

// Get keyborad
String strRslt;
int count = 0;
while (true){
count++;
if (1 < count){
break;
}
strRslt = oJniM.getKeyCode();
System.out.println("User input is :" + strRslt);
}

// Show panel
String pnlAry[][] = {
{"0", "0", "1", "0", "0", "0", "\n"},
{"0", "0", "1", "1", "1", "0", "\n"},
{"0", "0", "0", "0", "0", "0", "\n"},
{"0", "0", "0", "0", "0", "0", "\n"},
{"0", "0", "0", "0", "0", "0", "\n"},
{"0", "0", "0", "0", "0", "0", "\n"},
{"0", "0", "0", "0", "0", "0", "\n"}
};
oJniM.showPanel(pnlAry, 2, 7);
waitSpecifiedTime(800);
String pnlAry1[][] = {
{"0", "0", "0", "0", "0", "0", "\n"},
{"0", "0", "1", "0", "0", "0", "\n"},
{"0", "0", "1", "1", "1", "0", "\n"},
{"0", "0", "0", "0", "0", "0", "\n"},
{"0", "0", "0", "0", "0", "0", "\n"},
{"0", "0", "0", "0", "0", "0", "\n"},
{"0", "0", "0", "0", "0", "0", "\n"}
};
oJniM.showPanel(pnlAry1, 2, 7);
waitSpecifiedTime(800);
String pnlAry2[][] = {
{"0", "0", "0", "0", "0", "0", "\n"},
{"0", "0", "0", "0", "0", "0", "\n"},
{"0", "0", "1", "0", "0", "0", "\n"},
{"0", "0", "1", "1", "1", "0", "\n"},
{"0", "0", "0", "0", "0", "0", "\n"},
{"0", "0", "0", "0", "0", "0", "\n"},
{"0", "0", "0", "0", "0", "0", "\n"}
};
oJniM.showPanel(pnlAry2, 2, 7);
}

// 延时函数
public static void waitSpecifiedTime(int iTmLen)
{
try {
Thread.sleep(iTmLen);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}


使用注意

1.生成DLL文件的VS工程必须设置为Release模式;

2.生成的DLL文件版本,必须适配目标机操作系统的位数;

Created by 王彬

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  jni c语言 java