Java JNI与Kotlin/Native如何交互获取对象字段值?
Great question! Kotlin/Native's lack of a VM makes its JNI interaction model feel a bit counterintuitive compared to JVM-based Kotlin, but it's actually rooted in the same JNI fundamentals—just from the native side of the fence. Let's break this down.
How Kotlin/Native Interacts with JNI
Unlike JVM code that calls native libraries via JNI, Kotlin/Native is a native binary (compiled to machine code for your target platform) that can either initiate calls to JVM-based Java code, or be called by the JVM as a native library. The key enabler here is Kotlin/Native's interop toolchain:
- The
cinteroptool generates Kotlin bindings for C/C++ (and by extension, JNI) headers. This lets you call JNI functions directly from Kotlin/Native, just like you would in a C/C++ program. - The
kotlin.native.interoppackage provides utilities to manage JNI environments, object references, and cross-language memory safety.
Crucially, since Kotlin/Native doesn't run on a JVM, it has to explicitly connect to a running JVM instance (if calling Java code) or expose itself as a native library the JVM can load via standard JNI mechanisms.
Accessing Java Field Values from Kotlin/Native
When it comes to parsing Java field values, you have two main approaches—using auto-generated bindings (the easy, recommended path) or manual JNI calls (for fine-grained control).
1. Using Auto-Generated Interop Bindings
The cinterop tool can generate Kotlin wrappers for Java classes (via JNI headers) that abstract away low-level JNI calls like GetFieldID. Here's how it works:
- Create a
.deffile (e.g.,MyJavaClass.def) referencing the JNI headers and target Java class. - Run
cinteropto generate Kotlin bindings. This creates a Kotlin class that mirrors your Java class, with properties mapped directly to Java fields. - In your Kotlin/Native code, access the Java field as if it were a regular Kotlin property—no manual JNI boilerplate needed.
Example snippet:
// Auto-generated binding from cinterop class MyJavaClass : jobject { val myStringField: String get() = ... // Under the hood, this handles GetFieldID + GetObjectField } // Your Kotlin/Native code fun accessJavaField(env: JNIEnv, javaObj: MyJavaClass) { val fieldValue = javaObj.myStringField println("Java field value: $fieldValue") }
2. Manual JNI Calls (For Custom Scenarios)
If you need dynamic field access or fine-grained control, you can call JNI functions directly from Kotlin/Native—just like in C/C++. Here's a step-by-step example:
- Get the JNI Environment: If being called by the JVM, this is passed to your native function. If initiating the call, you'll need to attach to a running JVM instance.
- Retrieve the Class Reference: Use
FindClassto get thejclassfor your target Java class. - Fetch the Field ID: Call
GetFieldIDwith the field name and its JNI signature (e.g.,"Ljava/lang/String;"for a String). - Extract the Field Value: Use the appropriate JNI function (e.g.,
GetObjectFieldfor objects,GetIntFieldfor primitives) to retrieve the value.
Example snippet:
fun getJavaFieldManually(env: JNIEnv, javaObj: jobject) { // Get the Java class reference val javaClass = env.findClass("com/example/MyJavaClass") ?: error("Failed to locate target class") // Get the ID of the "myStringField" field val fieldId = env.getFieldID(javaClass, "myStringField", "Ljava/lang/String;") ?: error("Failed to retrieve field ID") // Extract the field value val fieldValueObj = env.getObjectField(javaObj, fieldId) as jstring val fieldValue = env.getStringUTF(fieldValueObj) println("Manual field access result: $fieldValue") // Clean up local references to avoid memory leaks env.deleteLocalRef(fieldValueObj) }
Key Differences from JVM-Based JNI
- No Built-In JVM: Kotlin/Native doesn't have a JVM running by default—you either attach to an existing JVM or launch one via
JNI_CreateJavaVMif initiating calls to Java. - Explicit Reference Management: Unlike the JVM's automatic garbage collection for Java objects, Kotlin/Native requires manual management of JNI local/global references to prevent leaks.
- Bindings Over Reflection: Auto-generated bindings eliminate most manual reflection calls (like
GetFieldID), making code cleaner and less error-prone.
内容的提问来源于stack exchange,提问作者Achrouf Abdenour

