mirror of
https://github.com/logos-messaging/nim-ffi.git
synced 2026-06-20 16:29:31 +00:00
An Android library module wrapping the timer library's native ABI behind an idiomatic Kotlin `TimerNode` class via a JNI shim. `build-libs.sh` cross-compiles two native libraries per ABI (arm64-v8a + x86_64) into src/main/jniLibs/: libmy_timer.so (the Nim library) and libmy_timer_jni.so (the JNI bridge, which NEEDs the former). The shim turns each Kotlin `external fun` into a blocking call and reads the typed EchoResponse struct out of the result callback. Gradle packages everything under jniLibs/ automatically; an instrumented test covers create/version/echo on a device/emulator. Native build validated for both ABIs (correct aarch64/x86_64 ELF, JNI symbols exported, libmy_timer.so linked). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Android example — Kotlin over the native C ABI (JNI)
An Android library module that wraps the timer library's native
(zero-serialization) C ABI behind an idiomatic Kotlin class, TimerNode, via a
small JNI shim. Struct returns come back as typed Kotlin values.
TimerNode("my-app").use { node ->
println(node.version()) // "nim-timer v0.1.0"
val r = node.echo("hello", delayMs = 5)
println("${r.echoed} / ${r.timerName}")
}
How it fits together
Kotlin TimerNode ──external fun──▶ libmy_timer_jni.so ──C ABI──▶ libmy_timer.so
(src/main/kotlin) (jni/my_timer_jni.c) (Nim, generated)
libmy_timer.so— the Nim library, exporting the native C ABI.libmy_timer_jni.so— a JNI shim that turns eachnativeKotlin method into a blocking call into that ABI, reading the typedEchoResponsestruct out of the result callback. ItNEEDSlibmy_timer.soat load time.
| Path | Description |
|---|---|
build-libs.sh |
Cross-compiles both .sos per ABI into src/main/jniLibs/<abi>/. |
jni/my_timer_jni.c |
The JNI bridge. |
src/main/kotlin/.../TimerNode.kt |
The Kotlin wrapper + EchoResult. |
build.gradle.kts, settings.gradle.kts |
Library module build files. |
src/androidTest/... |
Instrumented test (runs on a device/emulator). |
Build
cd examples/timer/android
export ANDROID_NDK_ROOT=/path/to/android-ndk # or rely on the default
./build-libs.sh # arm64-v8a + x86_64 by default
This stages libmy_timer.so + libmy_timer_jni.so under src/main/jniLibs/.
The Android Gradle plugin packages everything under jniLibs/ into the AAR/APK
automatically — no extra Gradle config needed.
Add ABIs by extending the table at the bottom of build-libs.sh and the
abiFilters in build.gradle.kts.
Use it
- As a module: drop
android/into your project,include(":mytimer")in yoursettings.gradle, andimplementation(project(":mytimer")). - By hand: copy
src/main/jniLibs/<abi>/*.sointo your app'sjniLibs/andTimerNode.ktinto your sources.
Then org.logos.mytimer.TimerNode is ready to use.
Run the test
./gradlew connectedAndroidTest # needs a connected device or emulator
Notes
- This is the native, same-process path — the app loads the library directly.
The CBOR ABI is for inter-process communication only (see
../ipc). - Each call blocks on a condvar in the JNI shim until the library's FFI-thread callback fires, so the Kotlin API is simple and synchronous.
- Regenerate
../c_bindings/my_timer.hwithnimble genbindings_c(from the repo root) if the library's API changes.