I have C headers for a C library I have compiled with the NDK for an Android device (androidNativeX64). I have used cinterop to create Kotlin bindings and would like to be able to call them from a Kotlin/Native Android App.
I know I could use JNI, but I want a way to access a C library via unmodified C stubs and I think this should be possible from everything I’ve read about cinterop.
The closest example I could find for this is kotlin-native/samples/androidNativeActivity which I got to run in an emulated Android device after a small fix (Issue #4004).
The current sample code only accesses a structure defined using cinterop, but does not link to a new C library. I added a new .def file in the example to also create a klib for my libbindings.a
library which has some new C code, in particular a test function called hello()
.
The modified src/x86main/main.kt now looks like this:
package sample.androidnative
import kotlinx.cinterop.*
import platform.android.*
import org.rustlightning.bindings.*fun main() {
val ret = hello(“test”)
… otherwise same as the original main.kt
I also added a new line to kotlin/compilations/cinterops of build.gradle.kts a reference my library called bindings
.
compilations["main"].cinterops {
val bindings by creating
val bmpformat by creating
}
The .def file for libbindings looks like the following, where libbindings.a is located at /Users/richard/VSCode/cinterop/bindings
.
package = org.rustlightning.bindings
headers = ffi.h
headerFilter = *
staticLibraries = libbindings.a
libraryPaths = /opt/local/lib /usr/local/opt/curl/lib /Users/richard/VSCode/cinterop/bindings
compilerOpts = -Isrc/nativeInterop/cinterop
compilerOpts.linux = -I/usr/include -I/usr/include/x86_64-linux-gnu
linkerOpts.osx = -L/opt/local/lib -Lsrc/nativeInterop/cinterop -l bindings
linkerOpts.linux = -L/usr/lib/x86_64-linux-gnu -l bindings
I copied libbindings.so
created with the NDK to ./build/Polyhedron/libs/x86_64 so it will be added to the apk file. It seems like this shouldn’t be necessary if I statically link libbindings.a in the .def file however.
I also added bindings.kt in src/x86main/kotlin that was created by cinterop for my ffi.h header file, where the definition of hello()
that was autogenerated looks like this:
@CCall("knifunptr_bindings0_hello")
external fun hello(@CCall.CString to: String?): CPointer<ByteVar>?
As per the README.md I then ran ./gradlew assemble
and adb install -r build/outputs/apk/debug/androidNativeActivity-debug.apk
to test it out.
The app crashes and I see this error in logcat:
java.lang.UnsatisfiedLinkError: Unable to load native library "/data/app/com.jetbrains.konan_activity2-HOQOXCz3Ejrum1yrc40ZlA==/lib/x86_64/libpoly.so": dlopen failed: cannot locate symbol "hello" referenced by "/data/app/com.jetbrains.konan_activity2-HOQOXCz3Ejrum1yrc40ZlA==/lib/x86_64/libpoly.so"...
Any ideas what I’m doing wrong? I’ve already spent way to much time on this problem now and wish I had some way to better debug what’s going on. I think this could be a really useful way to use Kotlin/native if it works, but I’m afraid I might be trying to do something that is for some reason impossible.
I don’t care about JVM calling right now, but I would expect there could be a way to integrate this with a multiplatform build if it works.
Thanks! Any advice would be greatly appreciated.