From 6c30c6386ccf5aa1db1f958ad528f4e6e72f33f4 Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Thu, 13 Aug 2020 14:41:37 +0200 Subject: [PATCH] app,app/internal: [Android] replace Window.Do with ViewEvent An event reporting the most recent Android View attached to the Window is both simpler to implement and automatically tracks the Activity lifecycle. The downside is that buggy programs may hold on to a stale references. Fortunately, JNI references are handles not pointers so the always-on Android JNI checks will very likely catch stale references on use. Signed-off-by: Elias Naur --- app/app_android.go | 41 ++----------------------------- app/internal/window/os_android.go | 39 +++++++++++------------------ 2 files changed, 16 insertions(+), 64 deletions(-) diff --git a/app/app_android.go b/app/app_android.go index e45eb3e7..cf6e9b7f 100644 --- a/app/app_android.go +++ b/app/app_android.go @@ -6,6 +6,8 @@ import ( "gioui.org/app/internal/window" ) +type ViewEvent = window.ViewEvent + // JavaVM returns the global JNI JavaVM. func JavaVM() uintptr { return window.JavaVM() @@ -16,42 +18,3 @@ func JavaVM() uintptr { func AppContext() uintptr { return window.AppContext() } - -// Do invokes the function with a JNI jobject handle to the underlying -// Android View. The function is invoked on the main thread, and the -// handle is invalidated after the function returns. -// -// Note: Do may deadlock if called from the same goroutine that receives from -// Events. -func (w *Window) Do(f func(view uintptr)) { - type androidDriver interface { - Do(f func(view uintptr)) bool - } - success := make(chan bool) - for { - driver := make(chan androidDriver) - // two-stage process: first wait for a valid driver... - go func() { - alive := w.driverDo(func() { - driver <- w.driver.(androidDriver) - }) - if !alive { - driver <- nil - } - }() - d := <-driver - if d == nil { - // Window is dead. - break - } - // .. then run the function on the main thread using the - // driver. The driver Do method returns false if the - // view was invalidated while switching to the main thread. - window.RunOnMain(func() { - success <- d.Do(f) - }) - if <-success { - break - } - } -} diff --git a/app/internal/window/os_android.go b/app/internal/window/os_android.go index 3458b261..dac0bf3f 100644 --- a/app/internal/window/os_android.go +++ b/app/internal/window/os_android.go @@ -82,6 +82,16 @@ type window struct { mpostFrameCallback C.jmethodID } +// ViewEvent is sent whenever the Window's underlying Android view +// changes. +type ViewEvent struct { + // View is a JNI global reference to the android.view.View + // instance backing the Window. The reference is valid until + // the next ViewEvent is received. + // A zero View means that there is currently no view attached. + View uintptr +} + type jvalue uint64 // The largest JNI type fits in 64 bits. var dataDirChan = make(chan string, 1) @@ -200,12 +210,14 @@ func Java_org_gioui_GioView_onCreateView(env *C.JNIEnv, class C.jclass, view C.j views[handle] = w w.loadConfig(env, class) w.setStage(system.StagePaused) + w.callbacks.Event(ViewEvent{View: uintptr(view)}) return handle } //export Java_org_gioui_GioView_onDestroyView func Java_org_gioui_GioView_onDestroyView(env *C.JNIEnv, class C.jclass, handle C.jlong) { w := views[handle] + w.callbacks.Event(ViewEvent{View: 0}) w.callbacks.SetDriver(nil) delete(views, handle) C.gio_jni_DeleteGlobalRef(env, w.view) @@ -532,23 +544,6 @@ func javaString(env *C.JNIEnv, str string) C.jstring { return C.gio_jni_NewString(env, (*C.jchar)(unsafe.Pointer(&utf16Chars[0])), C.int(len(utf16Chars))) } -// Do invokes the function with a global JNI handle to the view. If -// the view is destroyed, Do returns false and does not invoke the -// function. -// -// NOTE: Do must be invoked on the Android main thread. -func (w *window) Do(f func(view uintptr)) bool { - if w.view == 0 { - return false - } - runInJVM(javaVM(), func(env *C.JNIEnv) { - view := C.gio_jni_NewGlobalRef(env, w.view) - defer C.gio_jni_DeleteGlobalRef(env, view) - f(uintptr(view)) - }) - return true -} - func varArgs(args []jvalue) *C.jvalue { if len(args) == 0 { return nil @@ -653,14 +648,6 @@ func (w *window) ReadClipboard() { // Close the window. Not implemented for Android. func (w *window) Close() {} -// RunOnMain is the exported version of runOnMain without a JNI -// environement. -func RunOnMain(f func()) { - runOnMain(func(_ *C.JNIEnv) { - f() - }) -} - // runOnMain runs a function on the Java main thread. func runOnMain(f func(env *C.JNIEnv)) { go func() { @@ -682,3 +669,5 @@ func Java_org_gioui_Gio_scheduleMainFuncs(env *C.JNIEnv, cls C.jclass) { } } } + +func (_ ViewEvent) ImplementsEvent() {}