Hello! I am currently investigating the use of Kot...
# kotlin-native
s
Hello! I am currently investigating the use of Kotlin Native in my project, specifically for working with a native library both from the native code side and the Android module side. While researching, I came across information suggesting that Kotlin Native can generate JNI bindings through CInterop. However, I have found limited up-to-date information on this topic online. The only article I found was dated back to 2019 (https://jonnyzzz.com/blog/2019/12/15/jni-kotlin). I also found a few GitHub repositories, but they were not very recent. I would greatly appreciate it if you could provide an update on the current state of JNI CInterop in modern Kotlin Native. Additionally, I am curious to know if there are any official sample projects available that demonstrate the use of Kotlin JVM + Kotlin Native together. Apologies if my question was not clear, and I would be grateful for any clarifications or insights you can provide. Thank you!
🔥 1
o
K/N cinterop
jvm
flavour is highly undocumented and not usable in current state externally, though it powers K/N compiler 🙂 Regarding examples usage of native libraries from K/N and K/JVM here what I know: • realm-kotlin - https://github.com/realm/realm-kotlin/tree/main/packages/cinterop • zipline - https://github.com/cashapp/zipline/tree/trunk/zipline also I have some WIP prototype of
multiplatform cinterop
here - https://github.com/whyoleg/ffi-kotlin though it’s far from production ready now
s
Thank you for your prompt response and for providing the requested information. I will explore the resources and examples you mentioned.
I've taken a quick look at the materials you provided, and it has definitely piqued my interest in the topic. However, to fully understand how everything works together, I believe we need a more detailed explanation of the infrastructure involved. It seems like there is space for experimentation and further exploration in this area. 👍
o
that’s what Im doing in `ffi-kotlin`… 🙂 moreover, it has support for JDK Panama API and JS/WASM via emscripten, so POC works with all kotlin platforms. But there are a lot of things to automate now - this is something on what Im experimenting now, as well as improving
kotlinx.cinterop
API
s
As an Android developer, my primary focus is on JNI NDK for incorporating native code into Android applications. I'll be eagerly waiting for any exciting updates and advancements you make in this field. While my main interest lies in Android NDK, I appreciate your experimentation and improvements to kotlinx.cinterop API
o
while we are here, mini-poll for what you expect: • How do you want to use API? • What specific APIs do you want? • Do you want full blown automatically generated bindings (with some libraries it could be hundreds/thousands declarations), or just 1-2 specific declarations, which you will want to use?
s
How I see this, based on my needs: // Kotlin native module/source set
Copy code
// ImageProcessing.kt file 
@JniExport 
fun crunchPixels(image: ByteArray): ByteArray { 
    /*impl*/
    return newImage
}
// Android module/source set
Copy code
object ImageProcessing { 
    external fun crunchPixels(image: ByteArray): ByteArray 
}
This is an overly simplified sample. Ideally, I want to minimize the interaction with JNI/NDK machinery, move it behind the scene. The goal is to blur the line between JVM code and native code interaction. The approach is similar to writing code inside the common source set with Multiplatform. As long as we stay within the boundaries of the standard library and avoid passing platform-specific elements to and from native code, the native function calls are indistinguishable from any other function calls. Ideally, if we have an immutable data class in the common source set, containing only primitive data types, we should be able to pass and receive it without any JNI friction, just like in regular Kotlin code. // Common source set
Copy code
@JniShared // IDK, or Shared 
data class DataHolder(val foo: Int, val bar: String)
// JVM source set
Copy code
object NativeHost { 
    external fun bazz(data: DataHolder): DataHolder 
}
// Native source set
Copy code
// NativeHost.kt file 
@JniExport 
fun bazz(data: DataHolder): DataHolder { 
    val newDataHolder: DataHolder = process(data) 
    return newDataHolder 
}
Here, we interact with the given data class instance like in regular code, without the need for JNIEnvVar operations to extract the values and return it back. Although I may not have extensive experience in native development, I have attempted to describe a solution that could potentially simplify my workflow when working with native code. While it may be challenging or even impossible to achieve everything I mentioned, I believe that the ideas presented could greatly enhance my experience and streamline the process of integrating native components. 😄
l
I created a library a while back that works for Android NDK specifically. I tried to add general jvm support, but ran into some weirdness with expect/actual on CPointers. We used it in production in my last job, so it should have working bindings to all the methods you'll need. Feel free to open an issue if I missed anything. https://github.com/LandryNorris/JniUtils