Idiomatic Go bindings for the Java Native Interface and 53 Android Java API packages, auto-generated from YAML specs to ensure full coverage and easy maintenance.
For remote Android API access over gRPC, see jni-proxy.
This project is part of a family of three Go libraries that cover the major Android interface surfaces. Each wraps a different layer of the Android platform:
graph TD
subgraph "Go application"
GO["Go code"]
end
subgraph "Interface libraries"
NDK["<b>ndk</b><br/>C API bindings via cgo"]
JNI["<b>jni</b><br/>Java API bindings via JNI+cgo"]
AIDL["<b>binder</b><br/>Binder IPC, pure Go"]
end
subgraph "Android platform"
CAPI["NDK C libraries<br/>(libcamera2ndk, libaaudio,<br/>libEGL, libvulkan, ...)"]
JAVA["Java SDK<br/>(android.bluetooth,<br/>android.location, ...)"]
BINDER["/dev/binder<br/>kernel driver"]
SYSSVCS["System services<br/>(ActivityManager,<br/>PowerManager, ...)"]
end
GO --> NDK
GO --> JNI
GO --> AIDL
NDK -- "cgo / #include" --> CAPI
JNI -- "cgo / JNIEnv*" --> JAVA
AIDL -- "ioctl syscalls" --> BINDER
BINDER --> SYSSVCS
JAVA -. "internally uses" .-> BINDER
CAPI -. "some use" .-> BINDER
| Library | Interface | Requires | Best for |
|---|---|---|---|
| ndk | Android NDK C APIs | cgo + NDK toolchain | High-performance hardware access: camera, audio, sensors, OpenGL/Vulkan, media codecs |
| jni (this project) | Java Android SDK via JNI | cgo + JNI + JVM/ART | Java-only APIs with no NDK equivalent: Bluetooth, WiFi, NFC, location, telephony, content providers |
| binder | Binder IPC (system services) | pure Go (no cgo) | Direct system service calls without Java: works on non-Android Linux with binder, minimal footprint |
-
Start with ndk when the NDK provides a C API for what you need (camera, audio, sensors, EGL/Vulkan, media codecs). These are the lowest-latency, lowest-overhead bindings since they go straight from Go to the C library via cgo.
-
Use jni when you need a Java Android SDK API that the NDK does not expose. Examples: Bluetooth discovery, WiFi P2P, NFC tag reading, location services, telephony, content providers, notifications. JNI is also the right choice when you need to interact with Java components (Activities, Services, BroadcastReceivers) or when you need the gRPC remote-access layer.
-
Use binder when you want pure-Go access to Android system services without any cgo dependency. This is ideal for lightweight tools, CLI programs, or scenarios where you want to talk to the binder driver from a non-Android Linux system. AIDL covers the same system services that Java SDK wraps (ActivityManager, PowerManager, etc.) but at the wire-protocol level.
-
Combine them when your application needs multiple layers. For example, a streaming app might use ndk for camera capture and audio encoding, jni for Bluetooth controller discovery, and binder for querying battery status from a companion daemon.
All three libraries talk to the same Android system services, but through different paths:
- The NDK C APIs are provided by Google as stable C interfaces to Android platform features. Some (camera, sensors, audio) internally use binder IPC to talk to system services; others (EGL, Vulkan, OpenGL) talk directly to kernel drivers. The
ndklibrary wraps these C APIs via cgo. - The Java SDK uses binder IPC internally for system service access (BluetoothManager, LocationManager, etc.), routing calls through the Android Runtime (ART/Dalvik). The
jnilibrary calls into these Java APIs via the JNI C interface and cgo. - The AIDL binder protocol is the underlying IPC mechanism that system-facing NDK and Java SDK APIs use. The
binderlibrary implements this protocol directly in pure Go, bypassing both C and Java layers entirely.
import (
"github.com/AndroidGoLab/jni"
"github.com/AndroidGoLab/jni/app"
"github.com/AndroidGoLab/jni/bluetooth"
)
ctx, _ := app.NewContext(vm)
adapter, _ := bluetooth.NewAdapter(ctx)
defer adapter.Close()
fmt.Println("Enabled:", adapter.IsEnabled())
fmt.Println("Name:", adapter.GetName())
devices, _ := adapter.GetBondedDevices()
for _, d := range devices {
fmt.Printf(" %s (%s)\n", d.Name, d.Address)
}import (
"github.com/AndroidGoLab/jni/app"
"github.com/AndroidGoLab/jni/location"
)
ctx, _ := app.NewContext(vm)
mgr, _ := location.NewManager(ctx)
defer mgr.Close()
loc, _ := mgr.GetLastKnownLocation(location.GpsProvider)
if loc != nil {
fmt.Printf("lat=%.6f lon=%.6f\n", loc.Latitude, loc.Longitude)
}import "github.com/AndroidGoLab/jni/net/wifi"
mgr, _ := wifi.NewManager(ctx)
defer mgr.Close()
fmt.Println("WiFi enabled:", mgr.IsEnabled())
info, _ := mgr.GetConnectionInfo()
fmt.Println("SSID:", info.SSID)import "github.com/AndroidGoLab/jni/widget/toast"
toast.Show(ctx, "Hello from Go!", toast.LengthShort)More examples: examples/
53 Android API packages organized in a Java SDK-mirroring hierarchy:
The project converts Android Java API specifications into safe, idiomatic Go packages through four code generation stages:
flowchart TD
JNISPEC["spec/jni.yaml"]
JNIOVER["spec/overlays/jni.yaml"]
JNITMPL["templates/jni/"]
JAVASPEC["spec/java/*.yaml"]
JAVAOVER["spec/overlays/java/*.yaml"]
JAVATMPL["templates/java/"]
CAPI["capi/*.go"]
ROOT["*.go (VM, Env, Class, Object, ...)"]
PKGS["{package}/*.go (53 packages)"]
PROTO["proto/*/*.proto"]
GRPC["grpc/server/ + grpc/client/"]
JNISPEC --> S1
JNIOVER --> S1
JNITMPL --> S1
subgraph S1["jnigen"]
direction LR
S1D["Generates CGo bindings + idiomatic JNI types"]
end
S1 --> CAPI
S1 --> ROOT
JAVASPEC --> S2
JAVAOVER --> S2
JAVATMPL --> S2
subgraph S2["javagen"]
direction LR
S2D["Generates Android API wrapper packages"]
end
S2 --> PKGS
JAVASPEC --> S3
JAVAOVER --> S3
subgraph S3["protogen + grpcgen"]
direction LR
S3D["Generates .proto files + gRPC server/client"]
end
S3 --> PROTO
S3 --> GRPC
style JNISPEC fill:#fff3cd,color:#000
style JNIOVER fill:#fff3cd,color:#000
style JNITMPL fill:#fff3cd,color:#000
style JAVASPEC fill:#fff3cd,color:#000
style JAVAOVER fill:#fff3cd,color:#000
style JAVATMPL fill:#fff3cd,color:#000
style CAPI fill:#d4edda,color:#000
style ROOT fill:#cce5ff,color:#000
style PKGS fill:#cce5ff,color:#000
style PROTO fill:#d4edda,color:#000
style GRPC fill:#d4edda,color:#000
Legend: Yellow = hand-written inputs, Green = generated intermediates, Blue = final consumer-facing output.
-
Raw CGo Layer (
capi/) — Direct C bindings to JNI via vtable dispatch. Generated byjnigen. -
Idiomatic JNI Layer (root package) — Safe Go types:
VM,Env,Class,Object,Value,String,Array,MethodID,FieldID. All JNI exceptions converted to Go errors. Thread safety viaVM.Do(). Generated byjnigen. -
Android API Layer (53 packages) — High-level wrappers for Android system services. Each package provides a
Managertype obtained viaNewManager(ctx), with methods that call through JNI. Generated byjavagen. -
gRPC Layer — See jni-proxy for the gRPC remote access layer.
.
├── *.go # Core JNI types (VM, Env, Class, Object, ...)
├── capi/ # Raw CGo bindings to JNI C API
├── internal/ # Internal: exception handling, test JVM
│
├── app/ # Android API wrappers (Java SDK hierarchy)
│ ├── alarm/, download/, ...
├── bluetooth/
├── hardware/
│ ├── camera/, usb/, ...
├── media/
│ ├── audiomanager/, player/, ...
├── net/
│ ├── wifi/, nsd/, ...
├── os/
│ ├── power/, battery/, ...
├── ... # (53 packages total)
│
├── tools/ # Code generators
│ ├── jnigen/ # Core JNI + capi generation
│ ├── javagen/ # Android API package generation
│ ├── protogen/ # .proto file generation
│ └── grpcgen/ # gRPC server/client generation
├── spec/ # YAML API specifications
│ ├── jni.yaml # JNI C API spec
│ ├── java/ # Per-package Android API specs
│ └── overlays/ # Hand-written customizations
├── templates/ # Go text templates for code generation
│ ├── jni/
│ └── java/
│
├── ref/android/ # Reference .class files
└── examples/ # Per-package usage examples
| Target | Description | Requires |
|---|---|---|
make generate |
Run all generators | JDK + protoc |
make jni |
Generate core JNI + capi | JDK |
make java |
Generate Android API packages | -- |
make proto |
Generate .proto files | -- |
make protoc |
Compile .proto to Go stubs | protoc + protoc-gen-go |
make grpc |
Generate gRPC server/client | protoc |
make cli |
Generate jnicli cobra commands | protoc |
make test |
Run all tests | JDK |
make test-tools |
Run generator tests only | -- |
make build |
Cross-compile for android/arm64 | Android NDK |
make lint |
Run golangci-lint | golangci-lint |
make clean |
Remove all generated files | -- |
The JNI bindings and generated Android API packages are verified via jni-proxy E2E tests against real hardware.
Verified platforms (click to expand)
| Type | Device | Android | API | ABI | Build | Date | Passed | Total |
|---|---|---|---|---|---|---|---|---|
| Phone | Pixel 8a | 16 | 36 | arm64-v8a | BP4A.260205.001 | 2026-03-15 | 21 | 21 |
| Emulator | sdk_gphone64_x86_64 | 15 | 35 | x86_64 | 2026-03-14 | 21 | 21 |
Tests cover: JNI version, class operations, method lookup + call, string round-trip, object operations, handle lifecycle, field access, exception handling, and full integration workflows. Camera recording, microphone recording, GPS location, and battery status are also verified on the Pixel 8a.
- Create
spec/java/{package}.yamlwith the Java class, methods, andgo_importpath. - Optionally create
spec/overlays/java/{package}.yamlfor customizations (extra methods, type overrides, name overrides). - Run
make javato generate the Go package. - For gRPC support, see jni-proxy.