跨语言桥接技术:在 Java 中调用 C++ 函数
1 前言
在现代软件开发中,跨语言桥接技术成为了实现不同编程语言之间互操作的重要手段。尤其是在需要将高性能的 C++ 代码与易于开发的 Java 应用程序结合时,这种技术显得尤为重要。通过这种桥接技术,开发者可以充分利用 C++ 的性能优势,同时又能够借助 Java 的丰富生态系统和开发便利性。本文将探讨如何在 Java 中调用 C++ 函数,通过详细的示例代码演示跨语言桥接的实现过程,包括如何定义和调用 C++ 函数、如何处理数据类型的映射以及如何管理 C++ 对象的生命周期。希望通过这篇文章,读者能够掌握在 Java 应用中有效地集成 C++ 代码的方法,进而提升其开发效率和应用性能。
2 示例代码
2.1 myapi.h
# pragma once
# ifdef MYAPI_EXPORTS
# define MYAPI __declspec ( dllexport)
# else
# define MYAPI __declspec ( dllimport)
# endif
# include "jni.h"
# ifdef __cplusplus
extern "C" {
# endif
typedef struct {
int x;
int y;
} MyStruct;
MYAPI int AddNumbers ( int a, int b) ;
MYAPI double AddDoubles ( double a, double b) ;
MYAPI void AddDoublesByRef ( double * a, double * b, double * result) ;
MYAPI char GetCharValue ( char c) ;
MYAPI void PrintString ( const char * str) ;
MYAPI const char * GetChineseString ( ) ;
MYAPI MyStruct AddStructs ( MyStruct a, MyStruct b) ;
class MyClass {
public :
int value;
MyClass ( int v) : value ( v) { }
int GetValue ( ) const { return value; }
} ;
JNIEXPORT jint JNICALL Java_MyNative_addNumbers ( JNIEnv* env, jobject obj, jint a, jint b) ;
JNIEXPORT jdouble JNICALL Java_MyNative_addDoubles ( JNIEnv* env, jobject obj, jdouble a, jdouble b) ;
JNIEXPORT void JNICALL Java_MyNative_addDoublesByRef ( JNIEnv* env, jobject obj, jdoubleArray a, jdoubleArray b, jdoubleArray result) ;
JNIEXPORT jchar JNICALL Java_MyNative_getCharValue ( JNIEnv* env, jobject obj, jchar c) ;
JNIEXPORT void JNICALL Java_MyNative_printString ( JNIEnv* env, jobject obj, jstring str) ;
JNIEXPORT jstring JNICALL Java_MyNative_getChineseString ( JNIEnv* env, jobject obj) ;
JNIEXPORT void JNICALL Java_MyNative_freeString ( JNIEnv* env, jobject obj, jstring str) ;
JNIEXPORT jobject JNICALL Java_MyNative_addStructs ( JNIEnv* env, jobject obj, jobject structA, jobject structB) ;
JNIEXPORT jlong JNICALL Java_MyNative_createMyClass ( JNIEnv* env, jclass cls, jint value) ;
JNIEXPORT void JNICALL Java_MyNative_destroyMyClass ( JNIEnv* env, jclass cls, jlong objPtr) ;
JNIEXPORT jint JNICALL Java_MyNative_getMyClassValue ( JNIEnv* env, jclass cls, jlong objPtr) ;
# ifdef __cplusplus
}
# endif
2.2 myapi.cpp
# include "myapi.h"
# include <iostream>
# include <cstring>
# include <jni.h>
MYAPI int AddNumbers ( int a, int b) {
return a + b;
}
MYAPI double AddDoubles ( double a, double b) {
return a + b;
}
MYAPI void AddDoublesByRef ( double * a, double * b, double * result) {
* result = * a + * b;
}
MYAPI char GetCharValue ( char c) {
return c;
}
MYAPI void PrintString ( const char * str) {
std:: cout << str << std:: endl;
}
MYAPI const char * GetChineseString ( ) {
const char * str = u8"你好,世界!" ;
size_t len = strlen ( str) + 1 ;
char * result = new char [ len] ;
strcpy_s ( result, len, str) ;
return result;
}
MYAPI void FreeString ( const char * str) {
delete [ ] str;
}
MYAPI MyStruct AddStructs ( MyStruct a, MyStruct b) {
MyStruct result;
result. x = a. x + b. x;
result. y = a. y + b. y;
return result;
}
JNIEXPORT jint JNICALL Java_MyNative_addNumbers ( JNIEnv* env, jobject obj, jint a, jint b) {
return AddNumbers ( a, b) ;
}
JNIEXPORT jdouble JNICALL Java_MyNative_addDoubles ( JNIEnv* env, jobject obj, jdouble a, jdouble b) {
return AddDoubles ( a, b) ;
}
JNIEXPORT void JNICALL Java_MyNative_addDoublesByRef ( JNIEnv* env, jobject obj, jdoubleArray a, jdoubleArray b, jdoubleArray result) {
jdouble* aElements = env-> GetDoubleArrayElements ( a, NULL ) ;
jdouble* bElements = env-> GetDoubleArrayElements ( b, NULL ) ;
jdouble* resultElements = env-> GetDoubleArrayElements ( result, NULL ) ;
AddDoublesByRef ( aElements, bElements, resultElements) ;
env-> ReleaseDoubleArrayElements ( a, aElements, 0 ) ;
env-> ReleaseDoubleArrayElements ( b, bElements, 0 ) ;
env-> ReleaseDoubleArrayElements ( result, resultElements, 0 ) ;
}
JNIEXPORT jchar JNICALL Java_MyNative_getCharValue ( JNIEnv* env, jobject obj, jchar c) {
return GetCharValue ( c) ;
}
JNIEXPORT void JNICALL Java_MyNative_printString ( JNIEnv* env, jobject obj, jstring str) {
const char * nativeString = env-> GetStringUTFChars ( str, 0 ) ;
PrintString ( nativeString) ;
env-> ReleaseStringUTFChars ( str, nativeString) ;
}
JNIEXPORT jstring JNICALL Java_MyNative_getChineseString ( JNIEnv* env, jobject obj) {
const char * chineseString = GetChineseString ( ) ;
jstring result = env-> NewStringUTF ( chineseString) ;
FreeString ( chineseString) ;
return result;
}
JNIEXPORT jobject JNICALL Java_MyNative_addStructs ( JNIEnv* env, jobject obj, jobject structA, jobject structB) {
jclass structClass = env-> GetObjectClass ( structA) ;
jfieldID fidX = env-> GetFieldID ( structClass, "x" , "I" ) ;
jfieldID fidY = env-> GetFieldID ( structClass, "y" , "I" ) ;
int xA = env-> GetIntField ( structA, fidX) ;
int yA = env-> GetIntField ( structA, fidY) ;
int xB = env-> GetIntField ( structB, fidX) ;
int yB = env-> GetIntField ( structB, fidY) ;
MyStruct a = { xA, yA } ;
MyStruct b = { xB, yB } ;
MyStruct result = AddStructs ( a, b) ;
jobject newStruct = env-> AllocObject ( structClass) ;
env-> SetIntField ( newStruct, fidX, result. x) ;
env-> SetIntField ( newStruct, fidY, result. y) ;
return newStruct;
}
JNIEXPORT jlong JNICALL Java_MyNative_createMyClass ( JNIEnv* env, jclass cls, jint value) {
jlong ret = reinterpret_cast < jlong> ( new MyClass ( value) ) ;
std:: cout << "Java_MyNative_createMyClass" << ":" << ret << std:: endl;
return ret;
}
JNIEXPORT void JNICALL Java_MyNative_destroyMyClass ( JNIEnv* env, jclass cls, jlong objPtr) {
std:: cout << "Java_MyNative_destroyMyClass: " << ":" << objPtr << std:: endl;
MyClass* obj = reinterpret_cast < MyClass* > ( objPtr) ;
std:: cout << "Destroying MyClass with value: " << obj-> GetValue ( ) << std:: endl;
delete obj;
}
JNIEXPORT jint JNICALL Java_MyNative_getMyClassValue ( JNIEnv* env, jclass cls, jlong objPtr) {
MyClass* obj = reinterpret_cast < MyClass* > ( objPtr) ;
return obj-> GetValue ( ) ;
}
2.3 MyNative.java
public class MyNative {
static {
System . loadLibrary ( "Dll1" ) ;
}
public static native int addNumbers ( int a, int b) ;
public static native double addDoubles ( double a, double b) ;
public static native void addDoublesByRef ( double [ ] a, double [ ] b, double [ ] result) ;
public static native char getCharValue ( char c) ;
public static native void printString ( String str) ;
public static native String getChineseString ( ) ;
public static native void freeString ( String str) ;
public static native MyStruct addStructs ( MyStruct a, MyStruct b) ;
public static native long createMyClass ( long number) ;
public static native void destroyMyClass ( long handle) ;
public static native int getMyClassValue ( long handle) ;
public static class MyStruct {
public int x;
public int y;
}
public static class MyClass {
private long handle;
public MyClass ( int number) {
this . handle = createMyClass ( number) ; ;
}
public long getValue ( ) {
return getMyClassValue ( handle) ;
}
public void dispose ( ) {
destroyMyClass ( this . handle) ;
}
}
public static void main ( String [ ] args) {
int sum = addNumbers ( 5 , 3 ) ;
System . out. println ( "Result of adding numbers: " + sum) ;
double doubleSum = addDoubles ( 5.5 , 3.3 ) ;
System . out. println ( "Result of adding doubles: " + doubleSum) ;
double a = 5.5 ;
double b = 3.3 ;
double [ ] aArray = { a } ;
double [ ] bArray = { b } ;
double [ ] resultArray = new double [ 1 ] ;
addDoublesByRef ( aArray, bArray, resultArray) ;
System . out. println ( "Result of adding doubles by ref: " + resultArray[ 0 ] ) ;
char charResult = getCharValue ( 'A' ) ;
System . out. println ( "Result of GetCharValue: " + charResult) ;
printString ( "Hello from C++" ) ;
String chineseString = getChineseString ( ) ;
System . out. println ( "Chinese string from C++: " + chineseString) ;
MyStruct structA = new MyStruct ( ) ;
structA. x = 1 ;
structA. y = 2 ;
MyStruct structB = new MyStruct ( ) ;
structB. x = 3 ;
structB. y = 4 ;
MyStruct structResult = addStructs ( structA, structB) ;
System . out. println ( "Result of adding structs: (" + structResult. x + ", " + structResult. y + ")" ) ;
MyClass myClass = new MyClass ( 999 ) ;
long number = myClass. getValue ( ) ;
System . out. println ( "Value from MyClass: " + number) ;
myClass. dispose ( ) ;
}
}
javac -encoding UTF-8 .\ MyNative.java
java MyNative