通过JNI连接C++与Kotlin/Java类及双向对象传递可行性问询
Great questions—JNI absolutely lets you bridge C++ and Kotlin/Java classes, and bidirectional object passing is totally feasible. Let’s break this down step by step with concrete examples matching your class structure.
1. Linking C++ Classes with Kotlin/Java Using JNI
The core idea is to create a "bridge" layer where JNI translates between C++ objects and their Kotlin/Java counterparts. Here’s the workflow:
Step 1: Define Your Kotlin Classes
First, outline the Kotlin classes you want to map to C++:
// Kotlin: com/your/package/A.kt class A { var someField: Int = 0 // Add other fields/methods as needed } // Kotlin: com/your/package/B.kt class B { var a: A = A() var anotherField: String = "" }
Step 2: Generate JNI Header File
Use kotlinc (or javac for Java) to generate the JNI header that defines the bridge functions. For Kotlin:
kotlinc B.kt A.kt -h .
This creates a header file (e.g., com_your_package_B.h) with function signatures your C++ code will implement.
Step 3: Implement JNI Bridge in C++
Write C++ code that uses JNI methods to instantiate Kotlin objects and populate their fields from C++ objects. We’ll cover this in detail in the next section for bidirectional passing.
2. Bidirectional Object Passing
Yes, you can convert objects between C++ and Kotlin in both directions. Let’s use your A and B classes to demonstrate.
Example 1: C++ Object → Kotlin Object
Here’s how to take a C++ B instance and convert it to a Kotlin B object:
First, your C++ classes:
// C++ classes #include <string> class A { public: int someField; A() : someField(42) {} // Default value for demo }; class B { public: A a; std::string anotherField; B() : anotherField("Hello from C++") {} };
Then the JNI implementation to convert C++ B to Kotlin B:
#include <jni.h> #include "com_your_package_B.h" // Generated header JNIEXPORT jobject JNICALL Java_com_your_package_B_fromCpp(JNIEnv* env, jclass clazz) { // Create a C++ B object B cppB; // 1. Instantiate Kotlin B object jclass kotlinBClass = env->FindClass("com/your/package/B"); jmethodID kotlinBConstructor = env->GetMethodID(kotlinBClass, "<init>", "()V"); jobject kotlinB = env->NewObject(kotlinBClass, kotlinBConstructor); // 2. Instantiate and populate Kotlin A object from C++ A jclass kotlinAClass = env->FindClass("com/your/package/A"); jmethodID kotlinAConstructor = env->GetMethodID(kotlinAClass, "<init>", "()V"); jobject kotlinA = env->NewObject(kotlinAClass, kotlinAConstructor); jfieldID aSomeFieldID = env->GetFieldID(kotlinAClass, "someField", "I"); env->SetIntField(kotlinA, aSomeFieldID, cppB.a.someField); // 3. Attach Kotlin A to Kotlin B jfieldID bAFieldID = env->GetFieldID(kotlinBClass, "a", "Lcom/your/package/A;"); env->SetObjectField(kotlinB, bAFieldID, kotlinA); // 4. Populate other fields in Kotlin B jfieldID bAnotherFieldID = env->GetFieldID(kotlinBClass, "anotherField", "Ljava/lang/String;"); jstring jStr = env->NewStringUTF(cppB.anotherField.c_str()); env->SetObjectField(kotlinB, bAnotherFieldID, jStr); // Clean up local references (good practice to avoid leaks) env->DeleteLocalRef(kotlinAClass); env->DeleteLocalRef(jStr); return kotlinB; }
Example 2: Kotlin Object → C++ Object
Now let’s convert a Kotlin B object back to a C++ B instance:
JNIEXPORT void JNICALL Java_com_your_package_B_toCpp(JNIEnv* env, jclass clazz, jobject kotlinB) { // 1. Extract fields from Kotlin B jclass kotlinBClass = env->GetObjectClass(kotlinB); jfieldID bAFieldID = env->GetFieldID(kotlinBClass, "a", "Lcom/your/package/A;"); jfieldID bAnotherFieldID = env->GetFieldID(kotlinBClass, "anotherField", "Ljava/lang/String;"); // 2. Extract Kotlin A and its fields jobject kotlinA = env->GetObjectField(kotlinB, bAFieldID); jclass kotlinAClass = env->GetObjectClass(kotlinA); jfieldID aSomeFieldID = env->GetFieldID(kotlinAClass, "someField", "I"); jint someFieldValue = env->GetIntField(kotlinA, aSomeFieldID); // 3. Extract string field jstring jAnotherField = (jstring)env->GetObjectField(kotlinB, bAnotherFieldID); const char* anotherFieldCStr = env->GetStringUTFChars(jAnotherField, nullptr); // 4. Create and populate C++ B object B cppB; cppB.a.someField = someFieldValue; cppB.anotherField = std::string(anotherFieldCStr); // Use the C++ object as needed (e.g., log values) printf("C++ B: someField=%d, anotherField=%s\n", cppB.a.someField, cppB.anotherField.c_str()); // Clean up resources env->ReleaseStringUTFChars(jAnotherField, anotherFieldCStr); env->DeleteLocalRef(kotlinAClass); env->DeleteLocalRef(kotlinA); env->DeleteLocalRef(kotlinBClass); }
Key Notes for JNI Object Handling
- Type Signatures: Always use correct JNI type signatures (e.g.,
Iforint,Ljava/lang/String;forString,Lcom/your/package/A;for classA). Generate them withjavap -s com.your.package.B. - Memory Management: Local references (like
jobjectinstances) are automatically released when the JNI call ends, but explicitly callingDeleteLocalRefhelps avoid memory leaks in long-running calls. UseNewGlobalRefif you need to retain objects beyond the current call. - Error Handling: Add checks for null values (e.g., if
FindClassreturnsnullptr) to avoid crashes.
内容的提问来源于stack exchange,提问作者Flamur Berisha

