使用VS创建含有多个cpp文件组成的复杂JNI项目,并编译成.so库,最终由Linux下的Java native调用。请给出详细的代码举例和说明。
时间: 2024-03-21 14:44:36 浏览: 134
好的,这是一个比较复杂的JNI项目,需要注意的地方比较多,我会尽量详细地给出代码和说明。
首先,我们需要在VS中创建一个空的C++项目,命名为"JNIproject",然后在项目中添加两个cpp文件,分别命名为"JNItest.cpp"和"JNIfunction.cpp"。
在"JNIfunction.cpp"中,我们写一些C++函数,这些函数最终会被Java层调用。以下是一个简单的例子:
```cpp
#include <stdio.h>
#include "JNIfunction.h"
JNIEXPORT void JNICALL Java_com_example_JNItest_print(JNIEnv *env, jobject obj, jstring str) {
const char *s = env->GetStringUTFChars(str, NULL);
printf("%s\n", s);
env->ReleaseStringUTFChars(str, s);
}
JNIEXPORT jstring JNICALL Java_com_example_JNItest_reverse(JNIEnv *env, jobject obj, jstring str) {
const char *s = env->GetStringUTFChars(str, NULL);
char *r = new char[strlen(s) + 1];
for (int i = 0; i < strlen(s); i++) {
r[i] = s[strlen(s) - i - 1];
}
r[strlen(s)] = '\0';
jstring result = env->NewStringUTF(r);
delete[] r;
env->ReleaseStringUTFChars(str, s);
return result;
}
```
这里我们定义了两个函数,一个是"print"函数,它接收一个字符串并打印出来;另一个是"reverse"函数,它接收一个字符串并返回其反转后的结果。
然后我们需要在"JNIfunction.h"中声明这些函数:
```cpp
#ifndef _Included_JNIfunction
#define _Included_JNIfunction
#include <jni.h>
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT void JNICALL Java_com_example_JNItest_print(JNIEnv *, jobject, jstring);
JNIEXPORT jstring JNICALL Java_com_example_JNItest_reverse(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
```
接下来我们需要在"JNItest.cpp"中实现Java层调用C++层的接口:
```cpp
#include <jni.h>
#include "JNIfunction.h"
JNIEXPORT void JNICALL Java_com_example_JNItest_print(JNIEnv *env, jobject obj, jstring str) {
jclass cls = env->GetObjectClass(obj);
jmethodID mid = env->GetMethodID(cls, "printString", "(Ljava/lang/String;)V");
if (mid == NULL) {
return;
}
env->CallVoidMethod(obj, mid, str);
}
JNIEXPORT jstring JNICALL Java_com_example_JNItest_reverse(JNIEnv *env, jobject obj, jstring str) {
jclass cls = env->GetObjectClass(obj);
jmethodID mid = env->GetMethodID(cls, "reverseString", "(Ljava/lang/String;)Ljava/lang/String;");
if (mid == NULL) {
return NULL;
}
return (jstring) env->CallObjectMethod(obj, mid, str);
}
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
jclass cls = env->FindClass("com/example/JNItest");
if (cls == NULL) {
return -1;
}
static JNINativeMethod methods[] = {
{"print", "(Ljava/lang/String;)V", (void *) Java_com_example_JNItest_print},
{"reverse", "(Ljava/lang/String;)Ljava/lang/String;", (void *) Java_com_example_JNItest_reverse}
};
jint ret = env->RegisterNatives(cls, methods, sizeof(methods) / sizeof(methods[0]));
if (ret != JNI_OK) {
return -1;
}
return JNI_VERSION_1_6;
}
```
这里我们实现了Java层调用C++层的"print"和"reverse"函数,并在"JNI_OnLoad"函数中注册这些函数。
最后,我们需要在VS中设置项目的属性,以便将其编译成.so库。具体步骤如下:
1. 右键点击"JNIproject"项目,选择"属性"。
2. 在"常规"选项卡中,将"配置类型"设置为"动态库(.dll)",将"目标扩展名"设置为".so"。
3. 在"C/C++"选项卡中,将"附加包含目录"设置为JDK的include目录,例如:"C:\Program Files\Java\jdk1.8.0_271\include"。
4. 在"C/C++"选项卡中,将"附加库目录"设置为JDK的lib目录,例如:"C:\Program Files\Java\jdk1.8.0_271\lib"。
5. 在"C/C++"选项卡中,将"代码生成"选项卡中的"运行库"设置为"多线程DLL(/MD)"。
6. 在"链接器"选项卡中,将"附加依赖项"设置为"jvm.lib"。
7. 在"链接器"选项卡中,将"输出文件"设置为"JNItest.so"。
现在我们可以编译项目了。编译完成后,我们可以在"JNIproject\Debug"目录下找到生成的JNItest.so库。
最后,我们需要在Linux下编写Java代码,调用这个.so库。以下是一个简单的例子:
```java
package com.example;
public class JNItest {
static {
System.loadLibrary("JNItest");
}
private native void print(String str);
private native String reverse(String str);
public static void main(String[] args) {
JNItest jni = new JNItest();
jni.print("Hello JNI");
String rev = jni.reverse("Hello JNI");
System.out.println(rev);
}
}
```
这里我们通过静态代码块加载JNItest.so库,并实现了"print"和"reverse"函数的Java层接口。
希望这个例子能够帮助你理解如何在VS中创建含有多个cpp文件组成的复杂JNI项目,并编译成.so库,最终由Linux下的Java native调用。
阅读全文