C语言调用Java

说明

这是一个从【C➡️Java】的文章,并非是从【Java➡️C】。

编写Java

随便使用IDEA创建一个gradle的kotlin程序,写下一个方法

package org.example

object Main {
    @JvmStatic
    fun diff(str1: String, str2: String): String {
        return "Hello";
    }
}

修改gradle.build使用全量编译:

jar {
    from { configurations.compileClasspath.collect { it.isDirectory() ? it : zipTree(it) } }
}

编写C++

注意:这里的java.hjava.cpp是为自定义的文件名称,不是系统文件!

java.h

#include <jni.h>
#include <stdio.h>
#include <string>

/**
 * 初始化Java环境,只能调用一次
 * 
 * 
 */
JNIEnv* _java_create_jvm(JavaVM **jvm);

/**
 * 字符串进行比对
 * 
 */
std::string _java_diff(JNIEnv* env, std::string str1, std::string str2); 

/**
 * jstring 转换为 std::string
 * 
 */
std::string _java_jstring2string(JNIEnv *env, jstring jstr);

java.cpp

#include "java.h"

JNIEnv *_java_create_jvm(JavaVM **jvm)
{
    JNIEnv *env;
    JavaVMInitArgs args;
    JavaVMOption options;
    // ⬇️ 这里为上面gradlew程序构建的jar文件
    // ⬇️ 使用 [gradlew build] 构建在 build/libs/ 下面
    options.optionString = (char *)"-Djava.class.path=untitled2-1.0.0-SNAPSHOT.jar";
    args.version = JNI_VERSION_1_8;
    args.nOptions = 1;
    args.options = &options;
    args.ignoreUnrecognized = 0;
    JNI_CreateJavaVM(jvm, (void **)&env, &args);
    return env;
}

std::string _java_diff(JNIEnv *env, std::string pdf, std::string img)
{
    // 查找Java中的类,具体就是jni
    jclass clazz = env->FindClass("org/example/Main");
    jmethodID method = env->GetStaticMethodID(clazz, "diff", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
    jstring jstr = (jstring)env->CallStaticObjectMethod(clazz, method, env->NewStringUTF(pdf.c_str()), env->NewStringUTF(img.c_str()));
    return _java_jstring2string(env, jstr);
}


std::string _java_jstring2string(JNIEnv *env, jstring jstr) {
    const jclass stringClass = env->GetObjectClass(jstr);
    const jmethodID getBytes = env->GetMethodID(stringClass, "getBytes", "(Ljava/lang/String;)[B");
    const jbyteArray stringJbytes = (jbyteArray) env->CallObjectMethod(jstr, getBytes, env->NewStringUTF("UTF-8"));

    size_t length = (size_t) env->GetArrayLength(stringJbytes);
    jbyte* pBytes = env->GetByteArrayElements(stringJbytes, NULL);

    std::string ret = std::string((char *)pBytes, length);
    env->ReleaseByteArrayElements(stringJbytes, pBytes, JNI_ABORT);

    env->DeleteLocalRef(stringJbytes);
    env->DeleteLocalRef(stringClass);
    return ret;
}

main.cpp

#include "java.h"

int main()
{
    JavaVM *jvm;
    JNIEnv *env = _java_create_jvm(&jvm);
    if (env == NULL)
        return 1;
    std::string result = _java_diff(env, "1.pdf", "2.jpg");
    printf(result.c_str());
    printf("\n");
}

编译C++

我这里使用的是cmake来编译,具体编译脚本如下:

# 最低cmake版本
cmake_minimum_required(VERSION 3.7)

# 项目名称
project(main)

# 设置C++标准为 C++ 14
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_FLAGS  "-Wall -static-libstdc++")

# 查找Java和Jni依赖
# 此处可不引用Java,因为没有用Java功能
find_package(Java 1.8 REQUIRED)
find_package(JNI 1.8 REQUIRED)

# 打印JNI相关信息 可删除
if (JNI_FOUND)
    message (STATUS "JNI_INCLUDE_DIRS=${JNI_INCLUDE_DIRS}")
    message (STATUS "JNI_LIBRARIES=${JNI_LIBRARIES}")
endif()

# 导入Java和Jni头文件
include(UseJava)
include_directories(${JNI_INCLUDE_DIRS})

# 需要编译的源码文件
set(SOURCE_FILES main.cpp java.cpp)

# 使用 -ljli 代替 -ljvm 避免提示使用Java6
set(JLI_LIBRARY "$ENV{JAVA_HOME}/jre/lib/jli/libjli.dylib")

# 构建可执行程序
add_executable(${PROJECT_NAME} ${SOURCE_FILES})

# !! 连接jni库 !!
target_link_libraries(${PROJECT_NAME} ${JLI_LIBRARY} ${JNI_LIBRARIES})

运行

使用cmake编译之后,可以在build目录下生出mian的执行文件,将jar文件复制到同目录下,执行./mian就可看到效果。

MacBook-Pro:build sollyu$ ./main 
Hello

代码无需开源了,上面就是全部了。

问题

整体文章已经结束了,下面内容是过程中碰到的问题,也随手记录下来,希望能帮助后来人。

问题1

因为对cmake文件的不熟悉,刚开始出现了下面的报错信息:

[build] Undefined symbols for architecture x86_64:
[build]   "_JNI_CreateJavaVM", referenced from:
[build]       _java_create_jvm in java.c.o
[build] ld: symbol(s) not found for architecture x86_64
[build] collect2: error: ld returned 1 exit status
[build] ninja: build stopped: subcommand failed.

百度和谷歌了半天也没有找到解决办法,都是在说丢失libjvm.dylib,但是我明明已经连接了,却还是报错。最后还是在stackoverflow里找到了解决办法。

target_link_libraries(<example> ${JNI_LIBRARIES})

问题2

在执行build/main的时候出现下面的错误:

# 执行出现错误
$ build/main
No Java runtime present, requesting install.

# 查看Java版本
$ java -version
openjdk version "1.8.0_212-release"
OpenJDK Runtime Environment (build 1.8.0_212-release-1586-b4-5784211)
OpenJDK 64-Bit Server VM (build 25.212-b4-5784211, mixed mode)

# 查看JAVA_HOME
# 因为我想用Java8 偷懒直接用AndroidStudio里自带的JDK
$ echo $JAVA_HOME
/Users/sollyu/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/192.6392135/Android Studio.app/Contents/jre/jdk/Contents/Home

同时电脑还弹出一个对话框:

您需要安装旧Java SE 6运行环境才能打开“此Java应用程序”。
点按“更多信息…”来访问旧Java SE 6下载网站。

尝试点击「更多信息…」,会下载一个安装包,但是依然无法安装:


最后在这里找到了怎么解决stackoverflow,使用-ljli代替-ljvm,上面的cmake中写了。

参考文章

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,911评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,014评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,129评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,283评论 1 264
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,159评论 4 357
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,161评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,565评论 3 382
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,251评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,531评论 1 292
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,619评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,383评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,255评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,624评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,916评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,199评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,553评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,756评论 2 335