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>
73 lines
2.7 KiB
Markdown
73 lines
2.7 KiB
Markdown
# 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.
|
|
|
|
```kotlin
|
|
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 each `native` Kotlin method into a
|
|
blocking call into that ABI, reading the typed `EchoResponse` struct out of the
|
|
result callback. It `NEEDS` `libmy_timer.so` at load time.
|
|
|
|
| Path | Description |
|
|
|------|-------------|
|
|
| `build-libs.sh` | Cross-compiles both `.so`s 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
|
|
|
|
```sh
|
|
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
|
|
your `settings.gradle`, and `implementation(project(":mytimer"))`.
|
|
- **By hand**: copy `src/main/jniLibs/<abi>/*.so` into your app's `jniLibs/` and
|
|
`TimerNode.kt` into your sources.
|
|
|
|
Then `org.logos.mytimer.TimerNode` is ready to use.
|
|
|
|
## Run the test
|
|
|
|
```sh
|
|
./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`](../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.h` with `nimble genbindings_c` (from the
|
|
repo root) if the library's API changes.
|