Skip to content

Commit cb11a97

Browse files
xaionaro@dx.centerxaionaro@dx.center
authored andcommitted
fix: rewrite gomobile examples as real working code
Replace println-based documentation with actual code that: - Creates real NDK handles (sensor manager, looper, config) - Converts to int64 (simulating gomobile bind boundary) - Converts back via NewXFromUintPtr and verifies pointer match - Uses restored handles for real NDK operations Verified E2E on Pixel 8a (arm64) and emulator (x86_64).
1 parent 17acc46 commit cb11a97

2 files changed

Lines changed: 133 additions & 303 deletions

File tree

Lines changed: 52 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -1,135 +1,76 @@
1-
// Exposing NDK sensors to Java/Kotlin via gomobile bind.
1+
// gomobile bind sensor bridge: round-trip NDK handles through int64.
22
//
3-
// gomobile bind generates Java/Kotlin bindings for exported Go functions.
4-
// It supports signed integers, floats, strings, booleans, []byte, errors,
5-
// interfaces, and structs — but NOT unsafe.Pointer or uintptr.
3+
// Demonstrates the pattern for passing NDK sensor handles across the
4+
// Go/Java boundary via gomobile bind. gomobile bind does not support
5+
// unsafe.Pointer or uintptr, so native handles are transported as int64
6+
// (Java long).
67
//
7-
// To pass NDK handles across the Go/Java boundary, use int64 (Java long)
8-
// as the transport type and convert via uintptr inside Go:
8+
// This example obtains a real ASensorManager, converts it to int64
9+
// (simulating the gomobile boundary), converts back, and uses the
10+
// restored handle to query sensors — proving the round-trip is lossless.
911
//
10-
// func NewSensorBridge(managerHandle int64) *SensorBridge {
11-
// mgr := sensor.NewManagerFromUintPtr(uintptr(managerHandle))
12-
// return &SensorBridge{mgr: mgr}
13-
// }
14-
//
15-
// The Java side obtains native handles as long values (e.g. from JNI)
16-
// and passes them to Go:
17-
//
18-
// // Java
19-
// long sensorManagerPtr = nativeGetSensorManager();
20-
// SensorBridge bridge = Sensorbridge.newSensorBridge(sensorManagerPtr);
21-
// String name = bridge.defaultAccelerometerName();
22-
//
23-
// # Go library package (bindable via gomobile bind)
24-
//
25-
// The pattern for a gomobile-bindable Go package:
12+
// A gomobile-bindable Go package would expose these operations as
13+
// exported methods on a struct:
2614
//
27-
// // Package sensorbridge exposes NDK sensor access to Java/Kotlin.
28-
// // Build with: gomobile bind -target=android ./sensorbridge
29-
// package sensorbbridge
15+
// package sensorbridge
3016
//
31-
// import "github.com/AndroidGoLab/ndk/sensor"
17+
// type Bridge struct{ mgr *sensor.Manager }
3218
//
33-
// // SensorBridge wraps a sensor.Manager for cross-language use.
34-
// // Exported struct fields and methods with supported types are
35-
// // automatically exposed to Java/Kotlin by gomobile bind.
36-
// type SensorBridge struct {
37-
// mgr *sensor.Manager // unexported: invisible to Java
19+
// func NewBridge(h int64) *Bridge {
20+
// return &Bridge{mgr: sensor.NewManagerFromUintPtr(uintptr(h))}
3821
// }
3922
//
40-
// // NewSensorBridge creates a bridge from a raw ASensorManager handle.
41-
// // The handle is passed as int64 because gomobile bind does not
42-
// // support unsafe.Pointer or uintptr.
43-
// func NewSensorBridge(managerHandle int64) *SensorBridge {
44-
// mgr := sensor.NewManagerFromUintPtr(uintptr(managerHandle))
45-
// return &SensorBridge{mgr: mgr}
23+
// func (b *Bridge) AccelerometerName() string {
24+
// return b.mgr.DefaultSensor(sensor.Accelerometer).Name()
4625
// }
4726
//
48-
// // DefaultAccelerometerName returns the name of the default accelerometer.
49-
// func (b *SensorBridge) DefaultAccelerometerName() string {
50-
// s := b.mgr.DefaultSensor(sensor.Accelerometer)
51-
// return s.Name()
52-
// }
53-
//
54-
// // Handle returns the underlying ASensorManager handle for passing
55-
// // back to Java/JNI code.
56-
// func (b *SensorBridge) Handle() int64 {
27+
// func (b *Bridge) Handle() int64 {
5728
// return int64(b.mgr.UintPtr())
5829
// }
5930
//
60-
// # Java usage
61-
//
62-
// import sensorbbridge.SensorBridge;
63-
//
64-
// // Obtain the native ASensorManager* from JNI or the NDK.
65-
// long ptr = nativeGetSensorManager();
31+
// Java side:
6632
//
67-
// // Create the Go bridge, passing the handle as a long.
68-
// SensorBridge bridge = Sensorbbridge.newSensorBridge(ptr);
69-
//
70-
// // Call Go methods — gomobile generates Java proxy methods.
71-
// String name = bridge.defaultAccelerometerName();
72-
// Log.d("Sensor", "Accelerometer: " + name);
73-
//
74-
// // Retrieve the handle back (e.g. to pass to another native call).
75-
// long handle = bridge.handle();
76-
//
77-
// # Key rules
78-
//
79-
// - Use int64 (Java long) to transport native pointers across the boundary.
80-
// - Convert int64 <-> uintptr inside Go; never expose uintptr to gomobile.
81-
// - Keep NDK handle fields unexported — gomobile cannot serialize them.
82-
// - Exported methods must use only gomobile-supported types.
83-
// - Close/release NDK resources from Go, not from Java.
33+
// long ptr = nativeGetSensorManager(); // from JNI
34+
// Bridge b = Sensorbridge.newBridge(ptr);
35+
// String name = b.accelerometerName();
8436
//
8537
// This program must run on an Android device.
8638
package main
8739

8840
import (
89-
"fmt"
41+
"log"
9042

9143
"github.com/AndroidGoLab/ndk/sensor"
9244
)
9345

9446
func main() {
95-
fmt.Println("=== gomobile bind: sensor bridge ===")
96-
fmt.Println()
47+
log.Println("=== gomobile sensor bridge ===")
9748

98-
// Demonstrate the int64 <-> uintptr <-> NDK handle conversion chain.
99-
//
100-
// In a real app, the int64 comes from Java (JNI native handle).
101-
// Here we show the Go-side conversion pattern.
49+
// Step 1: Obtain a real sensor manager from the NDK.
50+
mgr := sensor.ASensorManager_getInstanceForPackage("com.example.gomobile")
51+
original := mgr.UintPtr()
52+
log.Printf("sensor manager obtained: 0x%x", original)
10253

103-
fmt.Println("Pattern: Java long -> Go int64 -> uintptr -> NDK handle")
104-
fmt.Println()
54+
// Step 2: Simulate the gomobile boundary — convert to int64.
55+
// In a real app, this int64 crosses the Go/Java boundary.
56+
javaHandle := int64(original)
57+
log.Printf("converted to int64 (Java long): %d", javaHandle)
10558

106-
// Step 1: Receive a handle from Java as int64.
107-
fmt.Println("Step 1: Java passes native handle as long (int64)")
108-
fmt.Println(" Java: long ptr = nativeGetSensorManager();")
109-
fmt.Println(" Java: SensorBridge bridge = Sensorbbridge.newSensorBridge(ptr);")
110-
fmt.Println()
59+
// Step 3: On the "Java side", pass the int64 back to Go.
60+
// Reconstruct the sensor.Manager from int64.
61+
restored := sensor.NewManagerFromUintPtr(uintptr(javaHandle))
62+
log.Printf("restored from int64: 0x%x", restored.UintPtr())
11163

112-
// Step 2: Convert int64 -> uintptr -> NDK type in Go.
113-
fmt.Println("Step 2: Go converts int64 -> uintptr -> sensor.Manager")
114-
fmt.Println(" Go: mgr := sensor.NewManagerFromUintPtr(uintptr(handle))")
115-
fmt.Println()
116-
117-
// Step 3: Use the NDK type normally.
118-
fmt.Println("Step 3: Use NDK types normally in Go")
119-
fmt.Println(" Go: s := mgr.DefaultSensor(sensor.Accelerometer)")
120-
fmt.Println(" Go: name := s.Name()")
121-
fmt.Println()
122-
123-
// Step 4: Return results to Java using supported types.
124-
fmt.Println("Step 4: Return results as gomobile-supported types (string, int64, error)")
125-
fmt.Println(" Go: return s.Name() // string -> Java String")
126-
fmt.Println(" Go: return int64(mgr.UintPtr()) // handle -> Java long")
127-
fmt.Println()
64+
// Step 4: Verify the round-trip — pointers must match.
65+
if restored.UintPtr() != original {
66+
log.Fatalf("FAIL: pointer mismatch: original=0x%x restored=0x%x", original, restored.UintPtr())
67+
}
68+
log.Println("round-trip OK: pointers match")
12869

129-
// Show the available sensor type constants that a bridge might expose.
130-
types := []struct {
131-
name string
132-
value sensor.Type
70+
// Step 5: Use the restored handle to query real sensors.
71+
sensorTypes := []struct {
72+
name string
73+
typeCode sensor.Type
13374
}{
13475
{"Accelerometer", sensor.Accelerometer},
13576
{"Gyroscope", sensor.Gyroscope},
@@ -138,25 +79,14 @@ func main() {
13879
{"Proximity", sensor.Proximity},
13980
}
14081

141-
fmt.Println("Sensor type constants (use int32 for gomobile):")
142-
for _, t := range types {
143-
fmt.Printf(" %-25s = %d\n", t.name, int32(t.value))
82+
for _, st := range sensorTypes {
83+
s := restored.DefaultSensor(st.typeCode)
84+
if s.UintPtr() == 0 {
85+
log.Printf(" %-15s not available", st.name)
86+
continue
87+
}
88+
log.Printf(" %-15s name=%q vendor=%q", st.name, s.Name(), s.Vendor())
14489
}
145-
fmt.Println()
146-
147-
// gomobile bind type mapping summary.
148-
fmt.Println("gomobile bind type mapping:")
149-
fmt.Println(" Go int64 <-> Java long (native handle transport)")
150-
fmt.Println(" Go string <-> Java String")
151-
fmt.Println(" Go int32 <-> Java int (sensor type constants)")
152-
fmt.Println(" Go float32 <-> Java float (sensor values)")
153-
fmt.Println(" Go float64 <-> Java double")
154-
fmt.Println(" Go bool <-> Java boolean")
155-
fmt.Println(" Go []byte <-> Java byte[]")
156-
fmt.Println(" Go error <-> Java Exception")
157-
fmt.Println(" Go struct <-> Java class (exported methods only)")
158-
fmt.Println(" Go interface <-> Java interface")
159-
fmt.Println()
16090

161-
fmt.Println("sensor-bridge example complete")
91+
log.Println("sensor-bridge done")
16292
}

0 commit comments

Comments
 (0)