说明
这是一个从【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.h
和java.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中写了。