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

7 篇文章 1 订阅

在软件开发过程中,常会出现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控制台工程
建立win32工程

选择工程输出为"DLL"

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++编译目录包含Jdk头文件目录

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

3.5编译生成dll文件:
启动编译

生成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 王彬
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值