Kotlin-JNI Help

➡️ Calling Native code from JVM

The @JNIConnect annotation is the core of the KSP module. It generates the necessary JNI boilerplate so you can write clean, idiomatic Kotlin code.

1. Write Your Kotlin/Native Function

Write your function using standard Kotlin types and annotate it with @JNIConnect.

import dev.datlag.nkommons.JNIConnect @JNIConnect( packageName = "your.package.name", className = "YourClass", // optional, defaults to function name (example). functionName = "customFunction" ) fun example(a: String, b: Boolean, c: CharArray, d: Double): String { return "$a, $b, $c, $d" }

2. Let KSP generate the JNI Stub

When you build the project, KSP will automatically generate a JNI-compatible function. You don't need to touch this generated file.

The following will be generated:

import dev.datlag.nkommons.JNIEnvVar import dev.datlag.nkommons.binding.jboolean import dev.datlag.nkommons.binding.jcharArray import dev.datlag.nkommons.binding.jdouble import dev.datlag.nkommons.binding.jobject import dev.datlag.nkommons.binding.jstring import dev.datlag.nkommons.utils.toJString import dev.datlag.nkommons.utils.toKBoolean import dev.datlag.nkommons.utils.toKCharArray import dev.datlag.nkommons.utils.toKString import kotlin.OptIn import kotlin.experimental.ExperimentalNativeApi import kotlin.native.CName import kotlinx.cinterop.CPointer import kotlinx.cinterop.ExperimentalForeignApi @OptIn(ExperimentalForeignApi::class, ExperimentalNativeApi::class) @CName("Java_your_package_name_YourClass_customFunction") public fun _exampleJNI( env: CPointer<JNIEnvVar>, clazz: jobject, p0: jstring, p1: jboolean, p2: jcharArray, p3: jdouble, ): jstring? { return example( p0.toKString(env) ?: return null, p1.toKBoolean(), p2.toKCharArray(env) ?: return null, p3 ).toJString(env) }

3. Declare and Use in JVM

Now, you can declare and call the original function in your Java or JVM Kotlin code as if it were a simple external method.

package your.package.name object YourClass { // same signature as native external fun customFunction( a: String, b: Boolean, c: CharArray, d: Double ): String }

Supported Types

The following types are currently supported for direct mapping:

Type

ArrayType

Boolean

BooleanArray

Byte

ByteArray

Char

CharArray

Double

DoubleArray

Float

FloatArray

Int

IntArray

Long

LongArray

Short

ShortArray

String

The following types will be mapped to a different Native-friendly type.

JVM

Native

java.nio.ByteBuffer

dev.datlag.nkommons.ByteBuffer

Using java.nio.ByteBuffer

You can pass direct ByteBuffer from JVM to Native easily:

// JVM / Android external fun fillBuffer(buffer: java.nio.ByteBuffer): Boolean fun main() { val buffer = ByteBuffer.allocateDirect(100) fillBuffer(buffer) }

On native side it will be mapped to dev.datlag.nkommons.ByteBuffer. You can access the address of the buffer, as well as its size.

// Native import dev.datlag.nkommons.ByteBuffer @JNIConnect( packageName = "org.example", className = "MainKt" ) fun fillBuffer(buffer: ByteBuffer): Boolean { val bufferAddress: CPointer<ByteVar> = buffer.address val bufferCapacity: Long = buffer.size Random.nextBytes(100).usePinned { randomBytes -> memcpy(buffer.address, randomBytes.addressOf(0), size.toULong()) } return true }
Last modified: 03 February 2026