➡️ Calling Native code from JVM
The @JniCall annotation is the core of the KSP module. It generates the necessary JNI boilerplate so you can write clean, idiomatic Kotlin code.
1. Define Your Kotlin/Native Function
Write your function using standard Kotlin types and annotate it with @JNICall.
// commonMain
@JniCall
expect fun mixed(number: Long, value: CharArray, upper: Boolean, char: Char): String
// nativeMain or jniNativeMain
actual fun mixed(number: Long, value: CharArray, upper: Boolean, char: Char): String
return "$a, $b, $c, $d"
}
2. Let KSP generate the JNI Stub
When you build the project, KSP will automatically generate:
actualfunctions for JVM/Android.native function to do the heavy JNI work.
public actual fun mixed(
number: Long,
`value`: CharArray,
upper: Boolean,
char: Char,
): String = mixedExternal(number, `value`, upper, char)
public external fun mixedExternal(
number: Long,
`value`: CharArray,
upper: Boolean,
char: Char,
): String
/**
* Calling user function kni.test.Bridge.mixed
* @param number kotlin.Long -> com.dshatz.kni.binding.jlong (via Simple)
* @param value kotlin.CharArray -> com.dshatz.kni.binding.jcharArray (via Convertable)
* @param upper kotlin.Boolean -> com.dshatz.kni.binding.jboolean (via Convertable)
* @param char kotlin.Char -> com.dshatz.kni.binding.jchar (via Convertable)
*
* @return kotlin.String <- com.dshatz.kni.binding.jstring
*/
@CName("Java_kni_test_Bridge_mixedExternal")
@OptIn(ExperimentalForeignApi::class, ExperimentalNativeApi::class)
public fun _mixedJNI(
env: CPointer<JNIEnvVar>,
clazz: jobject,
number: jlong,
`value`: jcharArray,
upper: jboolean,
char: jchar,
): jstring {
// generated by Kotlin-JNI
// this calls the actual fun you defined above in the native sourceset.
val env = env.GetAndAttach()!!
return (kni.test.Bridge.mixed(
number,
(`value`).toKCharArray(env)!!,
(upper).toKBoolean(env)!!,
(char).toKChar(env)!!
)).toJString(env)!!
}
3. Use in common code
Now you can call your mixed function from Android, JVM, Native or commonMain!
On non-native targets the call will be routed through JNI to your Native implementation.
fun main() {
mixed(Long.MAX_VALUE, " - max value".toCharArray(), false, 'x')
}
Last modified: 23 April 2026