app, cmd/gomobile: expose an app.State variable
The State can be used in the Start callback (and lazily-initialized packages) instead of JavaInit. This stores a reference to the android.context.Context for the (future) keyboard package and for setting TMPDIR. Second attempt at https://golang.org/cl/4400. Change-Id: I673997b26ab25ce5140cb31950593d8c6dbd9c51 Reviewed-on: https://go-review.googlesource.com/5555 Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
This commit is contained in:
parent
67dbb30552
commit
b2dee7a880
@ -13,7 +13,7 @@ import android.util.Log;
|
||||
// Go code.
|
||||
public final class Go {
|
||||
// init loads libgojni.so and starts the runtime.
|
||||
public static void init(Context context) {
|
||||
public static void init(final Context ctx) {
|
||||
if (Looper.myLooper() != Looper.getMainLooper()) {
|
||||
Log.wtf("Go", "Go.init must be called from main thread (looper="+Looper.myLooper().toString()+")");
|
||||
}
|
||||
@ -29,7 +29,7 @@ public final class Go {
|
||||
|
||||
new Thread("GoMain") {
|
||||
public void run() {
|
||||
Go.run();
|
||||
Go.run(ctx);
|
||||
}
|
||||
}.start();
|
||||
|
||||
@ -42,6 +42,6 @@ public final class Go {
|
||||
|
||||
private static boolean running = false;
|
||||
|
||||
private static native void run();
|
||||
private static native void run(Context ctx);
|
||||
private static native void waitForRun();
|
||||
}
|
||||
|
@ -15,16 +15,13 @@
|
||||
#include <string.h>
|
||||
#include "_cgo_export.h"
|
||||
|
||||
// Defined in android.go.
|
||||
extern pthread_cond_t go_started_cond;
|
||||
extern pthread_mutex_t go_started_mu;
|
||||
extern int go_started;
|
||||
extern JavaVM* current_vm;
|
||||
|
||||
// Defined in the Go runtime.
|
||||
static int (*_rt0_arm_linux1)(int argc, char** argv);
|
||||
|
||||
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||
current_vm = vm;
|
||||
current_ctx = NULL;
|
||||
current_native_activity = NULL;
|
||||
|
||||
JNIEnv* env;
|
||||
if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK) {
|
||||
@ -92,7 +89,10 @@ void InitGoRuntime() {
|
||||
|
||||
// Runtime entry point when using NativeActivity.
|
||||
void ANativeActivity_onCreate(ANativeActivity *activity, void* savedState, size_t savedStateSize) {
|
||||
// Note that activity->clazz is mis-named.
|
||||
current_vm = activity->vm;
|
||||
current_ctx = (*activity->env)->NewGlobalRef(activity->env, activity->clazz);
|
||||
current_native_activity = activity;
|
||||
|
||||
InitGoRuntime();
|
||||
|
||||
@ -121,7 +121,8 @@ void ANativeActivity_onCreate(ANativeActivity *activity, void* savedState, size_
|
||||
|
||||
// Runtime entry point when embedding Go in a Java App.
|
||||
JNIEXPORT void JNICALL
|
||||
Java_go_Go_run(JNIEnv* env, jclass clazz) {
|
||||
Java_go_Go_run(JNIEnv* env, jclass clazz, jobject ctx) {
|
||||
current_ctx = (*env)->NewGlobalRef(env, ctx);
|
||||
init_go_runtime(NULL);
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,12 @@ int go_started;
|
||||
// on android.
|
||||
JavaVM* current_vm;
|
||||
|
||||
// current_ctx is Android's android.context.Context. May be NULL.
|
||||
jobject current_ctx;
|
||||
|
||||
// current_native_activity is the Android ANativeActivity. May be NULL.
|
||||
ANativeActivity* current_native_activity;
|
||||
|
||||
// build_auxv builds an ELF auxiliary vector for initializing the Go
|
||||
// runtime. While there does not appear to be any spec for this
|
||||
// format, there are some notes in
|
||||
@ -166,10 +172,16 @@ func onConfigurationChanged(activity *C.ANativeActivity) {
|
||||
func onLowMemory(activity *C.ANativeActivity) {
|
||||
}
|
||||
|
||||
// JavaInit is an initialization function registered by the package
|
||||
// golang.org/x/mobile/bind/java. It gives the Java language
|
||||
// bindings access to the JNI *JavaVM object.
|
||||
var JavaInit func(javaVM uintptr)
|
||||
type androidState struct {
|
||||
}
|
||||
|
||||
func (androidState) JavaVM() unsafe.Pointer {
|
||||
return unsafe.Pointer(C.current_vm)
|
||||
}
|
||||
|
||||
func (androidState) AndroidContext() unsafe.Pointer {
|
||||
return unsafe.Pointer(C.current_ctx)
|
||||
}
|
||||
|
||||
var (
|
||||
windowDestroyed = make(chan bool)
|
||||
@ -227,6 +239,23 @@ func (a *asset) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func runStart(cb Callbacks) {
|
||||
State = androidState{}
|
||||
|
||||
if cb.Start != nil {
|
||||
cb.Start()
|
||||
}
|
||||
}
|
||||
|
||||
// notifyInitDone informs Java that the program is initialized.
|
||||
// A NativeActivity will not create a window until this is called.
|
||||
func notifyInitDone() {
|
||||
C.pthread_mutex_lock(&C.go_started_mu)
|
||||
C.go_started = 1
|
||||
C.pthread_cond_signal(&C.go_started_cond)
|
||||
C.pthread_mutex_unlock(&C.go_started_mu)
|
||||
}
|
||||
|
||||
func run(cb Callbacks) {
|
||||
// We want to keep the event loop on a consistent OS thread.
|
||||
runtime.LockOSThread()
|
||||
@ -237,20 +266,12 @@ func run(cb Callbacks) {
|
||||
C.free(unsafe.Pointer(ctag))
|
||||
C.free(unsafe.Pointer(cstr))
|
||||
|
||||
if JavaInit != nil {
|
||||
JavaInit(uintptr(unsafe.Pointer(C.current_vm)))
|
||||
}
|
||||
|
||||
// Inform Java that the program is initialized.
|
||||
C.pthread_mutex_lock(&C.go_started_mu)
|
||||
C.go_started = 1
|
||||
C.pthread_cond_signal(&C.go_started_cond)
|
||||
C.pthread_mutex_unlock(&C.go_started_mu)
|
||||
|
||||
for {
|
||||
select {
|
||||
case w := <-windowCreated:
|
||||
windowDrawLoop(cb, w, queue)
|
||||
}
|
||||
if C.current_native_activity == nil {
|
||||
runStart(cb)
|
||||
notifyInitDone()
|
||||
select {}
|
||||
} else {
|
||||
notifyInitDone()
|
||||
windowDrawLoop(cb, <-windowCreated, queue)
|
||||
}
|
||||
}
|
||||
|
22
app/app.go
22
app/app.go
@ -25,8 +25,12 @@ type Callbacks struct {
|
||||
// Start is called when the app enters the foreground.
|
||||
// The app will start receiving Draw and Touch calls.
|
||||
//
|
||||
// Window geometry will be configured and an OpenGL context
|
||||
// will be available.
|
||||
// If the app is responsible for the screen (that is, it is an
|
||||
// all-Go app), then Window geometry will be configured and an
|
||||
// OpenGL context will be available during Start.
|
||||
//
|
||||
// If this is a library, Start will be called before the
|
||||
// app is told that Go has finished initialization.
|
||||
//
|
||||
// Start is an equivalent lifecycle state to onStart() on
|
||||
// Android and applicationDidBecomeActive on iOS.
|
||||
@ -82,3 +86,17 @@ type ReadSeekCloser interface {
|
||||
io.ReadSeeker
|
||||
io.Closer
|
||||
}
|
||||
|
||||
// State is global application-specific state.
|
||||
//
|
||||
// The State variable also holds operating system specific state.
|
||||
// Android apps have the extra methods:
|
||||
//
|
||||
// // JavaVM returns a JNI *JavaVM.
|
||||
// JavaVM() unsafe.Pointer
|
||||
//
|
||||
// // AndroidContext returns a jobject for the app android.context.Context.
|
||||
// AndroidContext() unsafe.Pointer
|
||||
//
|
||||
// State is not valid until Run has been called.
|
||||
var State interface{}
|
||||
|
@ -101,6 +101,9 @@ func windowDrawLoop(cb Callbacks, w *C.ANativeWindow, queue *C.AInputQueue) {
|
||||
geom.Width = geom.Pt(float32(C.windowWidth) / geom.PixelsPerPt)
|
||||
geom.Height = geom.Pt(float32(C.windowHeight) / geom.PixelsPerPt)
|
||||
|
||||
// Wait until geometry and GL is initialized before cb.Start.
|
||||
runStart(cb)
|
||||
|
||||
// We start here rather than onStart so the window exists and the Gl
|
||||
// context is configured.
|
||||
if cb.Start != nil {
|
||||
|
@ -8,11 +8,11 @@ package main
|
||||
|
||||
import (
|
||||
"golang.org/x/mobile/app"
|
||||
"golang.org/x/mobile/bind/java"
|
||||
|
||||
_ "golang.org/x/mobile/bind/java"
|
||||
_ "golang.org/x/mobile/bind/java/testpkg/go_testpkg"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app.Run(app.Callbacks{})
|
||||
app.Run(app.Callbacks{Start: java.Init})
|
||||
}
|
||||
|
@ -78,10 +78,13 @@ func init() {
|
||||
res.out = make(map[int32]*seq.Buffer)
|
||||
}
|
||||
|
||||
func init() {
|
||||
app.JavaInit = func(javaVM uintptr) {
|
||||
C.init_seq(unsafe.Pointer(javaVM))
|
||||
}
|
||||
// Init initializes communication with Java.
|
||||
// Typically called from the Start callback in app.Run.
|
||||
func Init() {
|
||||
vm := app.State.(interface {
|
||||
JavaVM() unsafe.Pointer
|
||||
}).JavaVM()
|
||||
C.init_seq(vm)
|
||||
}
|
||||
|
||||
func seqToBuf(bufptr **C.uint8_t, lenptr *C.size_t, buf *seq.Buffer) {
|
||||
|
@ -295,13 +295,13 @@ var androidMainTmpl = template.Must(template.New("android.go").Parse(`
|
||||
package main
|
||||
|
||||
import (
|
||||
"golang.org/x/mobile/app"
|
||||
"golang.org/x/mobile/app"
|
||||
"golang.org/x/mobile/bind/java"
|
||||
|
||||
_ "golang.org/x/mobile/bind/java"
|
||||
_ "{{.}}"
|
||||
_ "{{.}}"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app.Run(app.Callbacks{})
|
||||
app.Run(app.Callbacks{Start: java.Init})
|
||||
}
|
||||
`))
|
||||
|
Loading…
x
Reference in New Issue
Block a user