C++ 通过 JNI 调用 Java 代码

简介

JNI 是 Java Native Interface 的缩写,它提供了若干的API实现了 Java 和其他语言(主要是 C&C++)的通信。

本篇主要介绍通过 C++ 调用 Java 代码的一般方法。

本例的开发环境为:WIN10 + VS2015 + JDK1.8.0_131

首先我们需要建立一个 Java 项目,为了简单起见,这里没有用到 IDE 。

建立 Java 项目

建立如下的目录结构:

JNITest\  -- 项目目录
  |-src\  -- 该目录保存源代码文件
  |  |-com\
  |     |-cynhard\
  |        |-test\
  |           |-Test.java
  |-classes\  -- 该目录保存 class 文件
  |-MANIFEST.MF  -- 打 jar 包时用到的配置文件

打开 Test.java 文件,编写如下代码。这里定义了两个简单的方法: add() 用来计算两个整数的和; toUpperCase() 用来将字符串转换为大写。Main() 方法对这两个函数进行了简单的测试。

package com.cynhard.test;

public class Test {

    public static void main(String[] args) {

        Test test = new Test();

        // test add
        System.out.printf("1 + 2 = %d\n", test.add(1, 2));

        // test toUpperCase
        String greeting = "hello, world!";
        System.out.printf("Upper case of \"%s\" is \"%s\"\n", 
            greeting, test.toUpperCase(greeting));
    }

    public int add(int num1, int num2) {
        return num1 + num2;
    }

    public String toUpperCase(String str) {
        return str.toUpperCase();
    }
}

接下来编译这个类。打开命令行,切换到 JNITest 目录,执行下面的命令进行编译。首先我们设置了一个环境变量 SRCPATH 用来指定源文件的目录。然后通过 javac 来进行编译,-sourcepath 用来指定源代码的目录。注意虽然用 -sourcepath 指定了源代码的目录,但后面仍然要指定 java 文件相对于当前目录的相对路径。 -d 用来指定 class 文件的输出目录。

> set SRCPATH=src\com\cynhard\test
> javac -sourcepath %SRCPATH% %SRCPATH%\Test.java -d classes

编译后,可以在 classes\com\cynhard\test目录下找到编译好的 Test.class 文件。可以通过以下命令执行这个文件。-cp 用来指定 class 文件目录。后面是用来执行的完整类名。

> java -cp classes com.cynhard.test.Test
1 + 2 = 3
Upper case of "hello, world!" is "HELLO, WORLD!"

接着我们将 class 文件打成 jar 包。

打开 MANIFEST.MF ,编写如下内容。Main-Class 指定了运行该 jar 包时应执行的类。注意后面必须有一个回车。

Main-Class: com.cynhard.test.Test

执行以下命令打 jar 包。-c 用来创建 jar 包。-v 用来打印详细信息。-f 指定 jar 包名称。-m 指定我们要包含 MANIFEST.MF 中的清单信息。-C 指定输入目录。

> jar -cvfm test.jar MANIFEST.MF -C classes .

打包后会在 JNITest 目录下找到 test.jar。可以通过如下命令执行这个 jar 包。

> java -jar test.jar
1 + 2 = 3
Upper case of "hello, world!" is "HELLO, WORLD!"

到这里我们的 Java 项目就建立完成了。下面开始使用 C++ 调用最后生成的这个 jar 包。

C++ 调用 Java 代码

打开 VS2015,新建一个控制台项目,在 main.cpp 中编写如下代码:

#include "jni.h"
#include <Windows.h>

int main()
{
    /* 设置 JVM 参数 */
    JavaVMInitArgs vmArgs;
    vmArgs.version = JNI_VERSION_1_8;
    const int OPTION_COUNT = 2;
    vmArgs.nOptions = OPTION_COUNT;
    JavaVMOption options[OPTION_COUNT] = { 0 };
    // classpath 指定为我们要调用的 jar 包路径
    options[0].optionString = "-Djava.class.path=G:\\projects\\java\\JNITest\\test.jar";
    options[1].optionString = "-Xmx1024m";  // 最大堆大小
    vmArgs.options = options;
    vmArgs.ignoreUnrecognized = JNI_TRUE;

    /* 启动 JVM */
    HMODULE hModule = LoadLibrary(TEXT("G:\\Java\\jre1.8.0_131\\bin\\server\\jvm.dll"));
    if (hModule == NULL)
    {
        return -1;
    }
    typedef jint (JNICALL *CreateJavaVMFuncPtr)(JavaVM **pvm, void **penv, void *args);
    CreateJavaVMFuncPtr CreateJavaVM = (CreateJavaVMFuncPtr)GetProcAddress(hModule, "JNI_CreateJavaVM");
    JavaVM *jvm = nullptr;
    JNIEnv *env = nullptr;
    jint res = (*CreateJavaVM)(&jvm, (void**)&env, &vmArgs);
    if (res < 0)
    {
        return -1;
    }

    /* 调用 class */
    // 找到 class
    jclass class_Test = env->FindClass("com/cynhard/test/Test");
    jmethodID methodId_main = env->GetStaticMethodID(class_Test, "main", "([Ljava/lang/String;)V");
    env->CallStaticVoidMethod(class_Test, methodId_main, "");

    // 释放资源
    jvm->DestroyJavaVM();
    FreeLibrary(hModule);

    return 0;
}
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页