• C++ 跨语言调用 Java


    C++ 跨语言调用 Java  

      Java JDK 提供了 JNI 接口供 C/C++ 程序调用 Java 编译后的类与方法,主要依赖于头文件(jni.h) 和 动态库(jvm.so/jvm.dll),由于 JNI 包含了丰富的接口映射和跨语言的数据通信,非常复杂(坑 深不见底),所以这里只对一个测试程序进行简单的描述。

      最开始测试的时候选择了 Window7 64 的环境,安装的 Java JDK 也是64位的,但是我们都知道 VS 编译的程序默认情况下都是32位程序,所以我在 LoadLibrary(“jvm.dll”)的时候总是失败,所以就放弃了 Windows 环境下的测试(懒得去编译64的程序),最终我使用了 CentOS7 64 完成了测试,并且测试的 Java 版本是 1.7(系统自带openjdk)。

    测试的 Java 代码如下所示。

    1 public class MyTest {
    2     private static int magic_counter = 777;
    3 
    4     public static void callback() {
    5     System.out.println("Hello world in java from cplusplus");
    6     System.out.print("Magic number: ");
    7     System.out.println(magic_counter);
    8     }
    9 }

      编写完 Java 测试程序之后,使用命令:javac MyTest.java 对 Java 类进行编译,并生成相应的 MyTest.class 文件,这个文件所在的位置非常重要,这关系到我们的 C++ JNI 程序能否找到这个文件,因为我们在 C++ JNI 程序中指定了 "-Djava.class.path=." 就是C++程序运行的当前目录,所有这里我们需要把 MyTest.class 文件拷贝到 C++ 程序的运行目录中。

      此外由于 JNI 函数需要将调用对象的 Signature ID 传入,所以我们还需要知道你所有使用对象的 Signature。通过命令: javap -s -p MyTest.class 命令可以获取后所有函数与变量的 Signature。如下图所示。

      在编译程序之前,需要了解你机器上的 Java 版本,及 jni.h 和 jvm.so 所在的位置,方便程序编译时能够找到相应的 Java 依赖,这里我使用了 CMake 来辅助编译,相应的 CMakeList.txt 如下所示。

    cmake_minimum_required(VERSION 3.5)
    project(testjni)
    
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
    
    include_directories(
            #/opt/program/jdk1.8.0_121/include/linux
            #/opt/program/jdk1.8.0_121/include
            /usr/lib/jvm/java-1.7.0-openjdk-1.7.0.111-2.6.7.2.el7_2.x86_64/include/linux
            /usr/lib/jvm/java-1.7.0-openjdk-1.7.0.111-2.6.7.2.el7_2.x86_64/include
    
    )
    link_directories( #
    /opt/program/jdk1.8.0_121/jre/lib/amd64/server /usr/lib/jvm/java-1.7.0-openjdk-1.7.0.111-2.6.7.2.el7_2.x86_64/jre/lib/amd64/server/ ) set(SOURCE_FILES main.cpp) add_executable(testjni ${SOURCE_FILES}) target_link_libraries( testjni jvm )

       下面是 C++ JNI 程序的简单示例,如下所示。

    #include <iostream>
    #include <jni.h>
    #include <memory.h>
    
    int main()
    {
        char opt1[] = "-Djava.compiler=NONE"; /** 暂时不知道啥意思,网上抄来的 */
        char opt2[] = "-Djava.class.path=.";  /** 指定Java类编译后.class文件所在的目录 */
        char opt3[] = "-verbose:NONE";        /** 暂时不知道啥意思,网上抄来的 */
    
        JavaVMOption options[3];
        options[0].optionString = opt1; options[0].extraInfo = NULL;
        options[1].optionString = opt2; options[1].extraInfo = NULL;
        options[2].optionString = opt3; options[2].extraInfo = NULL;
    
        JavaVMInitArgs jargv;
        jargv.version = JNI_VERSION_1_6; /** JDK JNI VERSION*/
        jargv.nOptions = 3;
        jargv.options = options;
        jargv.ignoreUnrecognized = JNI_TRUE;
    
        JavaVM* jvm = NULL;
        JNIEnv* jenv = NULL;
        jint res = JNI_CreateJavaVM( &jvm, (void**)&jenv, &jargv );
        if ( 0 != res )
            return 1;
    
        jclass jc = jenv->FindClass( "MyTest" );
        if ( NULL == jc )
            return 1;
    
        jmethodID jmid = jenv->GetStaticMethodID( jc, "callback", "()V" );
        if ( NULL == jmid )
            return 1;
    
        jenv->CallStaticVoidMethod( jc, jmid );
    
    
        /** 在网上没有找到任何关于空间相关 JavaVM 和 JNIEnv 资源释放的描述 */
        std::cout << "Hello, World!" << std::endl;
        return 0;
    }

      最终输出的结果与预期的一致,就到这里,在这么深的坑里,祝大家好运哦。

  • 相关阅读:
    《Java从入门到精通》第十三章学习笔记
    Java实训项目--影碟管理软件
    《Java从入门到精通》第十二章学习笔记
    《Java从入门到精通》第十一章学习笔记
    《Java从入门到精通》第十章学习笔记
    《Java从入门到精通》第九章学习笔记
    Swift游戏实战-跑酷熊猫 02 创建熊猫类
    Swift游戏实战-跑酷熊猫 01 创建工程导入素材
    Swift游戏实战-跑酷熊猫 00 游戏预览
    Swift游戏实战-跑酷熊猫(一) 简介 (含源代码)
  • 原文地址:https://www.cnblogs.com/wanghaiyang1930/p/6604319.html
Copyright © 2020-2023  润新知