2
0
mirror of synced 2025-02-22 14:28:14 +00:00

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:
David Crawshaw 2015-02-22 15:27:20 -05:00
parent 67dbb30552
commit b2dee7a880
8 changed files with 87 additions and 41 deletions

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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)
}
}

View File

@ -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{}

View File

@ -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 {

View File

@ -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})
}

View File

@ -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) {

View File

@ -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})
}
`))