Linux下在C++中使用JNI调用jar包

最近在研究C++中如何调用jar包。网上的JNI教程绝大部分都是讲的如何在Java里面调用C++编译好的so,而反过来的比较少。综合了一些网上的资料在自己的linux devbox上面把简单的C++调用jar的路径调通了,这里记录下步骤,文末会贴出过程中参考的文章链接。

配置环境

列下我这边devbox上的环境

  • Debian GNU/Linux 9
  • openjdk version "1.8.0_332"
  • g++ (Debian 6.3.0-18+deb9u1) 6.3.0 20170516

安装Java的过程这里就不详细列出了,可以参考https://linuxize.com/post/install-java-on-debian-9/'

安装完Java以后打开 /etc/profile,在文件最后添加

export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JAVA_HOME/jre/lib/rt.jar

然后运行 source /etc/profile 使配置文件立即生效。

查看下各个环境变量的值:

$ java -version
openjdk version "1.8.0_332"
OpenJDK Runtime Environment (build 1.8.0_332-8u332-ga-1~deb9u1-b09)
OpenJDK 64-Bit Server VM (build 25.332-b09, mixed mode)


$ echo $JAVA_HOME
/usr/lib/jvm/java-8-openjdk-amd64

编写Java程序并打成Jar包

接下来我们可以编写一个简单的Java程序。首先让我们先创建一个目录~/jni_example,后面所有的路径都会以这个目录为根目录。

~$ mkdir jni_example
~$ cd jni_example/

接下来我们在jni_example目录下面创建一个文件夹java_code用来存放我们接下来要编写的java文件。

~/jni_example$ mkdir java_code
~/jni_example$ cd java_code/
~/jni_example/java_code$ 

在~/jni_example/java_code这条路径下面,我们创建一系列路径以及最终的java文件。

~/jni_example/java_code$ mkdir -p com/example/javatest
~/jni_example/java_code$ cd com/example/javatest/
~/jni_example/java_code/com/example/javatest$ touch Test.java

在Test.java里面,我们创建一个Test类并添加一个静态方法和一个普通方法。

package com.example.javatest;

public class Test {
    public Test(){
    }

    public static int add(int a, int b) {
        return a + b;
    }

    public boolean reverse(boolean value) {
        return !value;
    }
}

然后我们编译这个java文件并且把编译好的.class文件封装成一个jar包。

~/jni_example/java_code/com/example/javatest$ javac Test.java
~/jni_example/java_code/com/example/javatest$ ls
Test.class  Test.java
~/jni_example/java_code$ cd ~/jni_example/java_code/
~/jni_example/java_code$ ls
com
~/jni_example/java_code$ jar cvf test.jar com/example/javatest/Test.class
added manifest
adding: com/example/javatest/Test.class(in = 355) (out= 266)(deflated 25%)
~/jni_example/java_code$ ls
com  test.jar

这样我们就把上面的java文件封装到了~/jni_example/java_code/test.jar这个jar包里面了。

编写C++程序并使用JNI调用jar包

接下来我们在~/jni_example这个目录下面创建一个存放cpp文件的路径

~/jni_example$ mkdir cpp_code
~/jni_example$ cd cpp_code/
~/jni_example/cpp_code$ 

然后我们在~/jni_example/cpp_code下创建一个文件 mytest.cpp,文件的示例代码如下

#include <stdio.h>
#include <iostream>
#include <jni.h>
using namespace std;

int main()
{
    JavaVMOption options[2];
    JNIEnv *env;
    JavaVM *jvm;
    JavaVMInitArgs vm_args;
    long status;
    jclass cls;
    jmethodID mid;
    jint jsum;
    jboolean jnot;
    jobject jobj;

    options[0].optionString = (char*)"-Djava.compiler=NONE";
    options[1].optionString = (char*)"-Djava.class.path=.:test.jar"; //在这里添加要使用的jar包

    vm_args.version = JNI_VERSION_1_8;
    vm_args.nOptions = 2;
    vm_args.options = options;
    vm_args.ignoreUnrecognized = JNI_TRUE;

    status = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);

    if (status != JNI_ERR) {
        cout << "Succeeded creating JVM " << endl;
        cls = env->FindClass("com/example/javatest/Test");  // 这里是jar包内Test类的具体路径
        if (cls != 0) {
            cout << "Succeeded finding target Java class" << endl;
            // 调用构造函数创建对象
            mid = env->GetMethodID(cls,"<init>","()V");
            if (mid != 0) {
                jobj = env->NewObject(cls, mid);
                // 如果想要捕获异常,可以使用注释中的语句
                /*if (env->ExceptionOccurred()) {
                    env->ExceptionDescribe();
                    env->ExceptionClear();
                    return 0;
                }*/
                cout << "Succeeded creating object using java constructor" << endl;
            }

            // 调用add函数
            mid = env->GetStaticMethodID(cls, "add", "(II)I");
            if (mid != 0) {
                jsum = env->CallStaticIntMethod(cls, mid, 100, 28);
                cout << "Result of calling add method is " << jsum << endl;
            }

            // 调用reverse函数
            mid = env->GetMethodID(cls, "reverse","(Z)Z");
            if(mid != 0) {
                jnot = env->CallBooleanMethod(jobj, mid, 1);
                cout << "Result of calling reverse method is " << jnot << endl;
            }
        }
        else{
            fprintf(stderr, "FindClass failed\n");
        }

        jvm->DestroyJavaVM();
        fprintf(stdout, "Java VM destory.\n");
        return 0;
    }
    else{
        cout << "Failed creating JVM " << endl;
        return -1;
    }
}

然后我们把之前编译好的jar包copy到当前目录下(这一步其实不用做,但是我这边不方便在上面的java classpath中引入绝对路径,所以简单点就把这个jar包拷到c++文件的目录下。

~/jni_example/cpp_code$ cp ~/jni_example/java_code/test.jar .
~/jni_example/cpp_code$ ls
mytest.cpp  test.jar

在编译c++文件之前,我们还需要设置另一个环境变量

~/jni_example/cpp_code$ export LD_LIBRARY_PATH=$JAVA_HOME/jre/lib/amd64/server/

然后我们用下面的命令编译c++程序

~/jni_example/cpp_code$ g++ -o mytest mytest.cpp -I ${JAVA_HOME}/include -I ${JAVA_HOME}/include/linux -L ${JAVA_HOME}/jre/lib/amd64/server -ljvm

运行C++程序

完成上面的步骤之后,我们就可以运行编译好的C++程序了

~/jni_example/cpp_code$ ./mytest 
Succeeded creating JVM 
Succeeded finding target Java class
Succeeded creating object using java constructor
Result of calling add method is 128
Result of calling reverse method is 
Java VM destory.

引用

这篇简易教程中参考了下面的一些blog。如果想要了解上面C++文件中一些参数的意义,或者想要从命令行中通过C++传递参数给Java方法,都可以参考我列出来的这些文章。

linux下通过JNI用C/C++中调用JAVA类_qq_14898543的博客-CSDN博客

基于jni实现c/c++调用jar包_vah101的博客-CSDN博客_c++ jni调用jar

Linux上c++通过JNI调用java代码笔记_Tjmies的博客-CSDN博客

如何在c++中调用java代码(转载) - Java世界 - BlogJava

[技术小贴士]: 用C/C++调用java

C++项目通过JNI使用Java第三方jar包_Louka的博客-CSDN博客

C/C++如何调用Java_笑看红尘花落一梦的博客-CSDN博客_c++调用java

基本JNI调用技术(c/c++与java互调)-阿里云开发者社区

Calling Java from C++ with JNI - CodeProject


PS:

1. 在C++中,如果传入的是jobject需要通过GetObjectClass(jobject)获取jclass, 而如果没有jobject需要通过FindClass("javapackage/class") 来获类的jclass

2.对于java方法的类型,可以通过javap -p -s [package].[classname]来获得。比如:

~/jni_example/java_code$ javap -p -s com.example.javatest.Test
Compiled from "Test.java"
public class com.example.javatest.Test {
  public com.example.javatest.Test();
    descriptor: ()V

  public static int add(int, int);
    descriptor: (II)I

  public boolean reverse(boolean);
    descriptor: (Z)Z
}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值