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 <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2020-08-13 14:41:37 +02:00
parent c3574cdabd
commit 6c30c6386c
2 changed files with 16 additions and 64 deletions
+2 -39
View File
@@ -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
}
}
}
+14 -25
View File
@@ -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() {}