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.
|
// Go code.
|
||||||
public final class Go {
|
public final class Go {
|
||||||
// init loads libgojni.so and starts the runtime.
|
// 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()) {
|
if (Looper.myLooper() != Looper.getMainLooper()) {
|
||||||
Log.wtf("Go", "Go.init must be called from main thread (looper="+Looper.myLooper().toString()+")");
|
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") {
|
new Thread("GoMain") {
|
||||||
public void run() {
|
public void run() {
|
||||||
Go.run();
|
Go.run(ctx);
|
||||||
}
|
}
|
||||||
}.start();
|
}.start();
|
||||||
|
|
||||||
@ -42,6 +42,6 @@ public final class Go {
|
|||||||
|
|
||||||
private static boolean running = false;
|
private static boolean running = false;
|
||||||
|
|
||||||
private static native void run();
|
private static native void run(Context ctx);
|
||||||
private static native void waitForRun();
|
private static native void waitForRun();
|
||||||
}
|
}
|
||||||
|
@ -15,16 +15,13 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "_cgo_export.h"
|
#include "_cgo_export.h"
|
||||||
|
|
||||||
// Defined in android.go.
|
// Defined in the Go runtime.
|
||||||
extern pthread_cond_t go_started_cond;
|
|
||||||
extern pthread_mutex_t go_started_mu;
|
|
||||||
extern int go_started;
|
|
||||||
extern JavaVM* current_vm;
|
|
||||||
|
|
||||||
static int (*_rt0_arm_linux1)(int argc, char** argv);
|
static int (*_rt0_arm_linux1)(int argc, char** argv);
|
||||||
|
|
||||||
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||||
current_vm = vm;
|
current_vm = vm;
|
||||||
|
current_ctx = NULL;
|
||||||
|
current_native_activity = NULL;
|
||||||
|
|
||||||
JNIEnv* env;
|
JNIEnv* env;
|
||||||
if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK) {
|
if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK) {
|
||||||
@ -92,7 +89,10 @@ void InitGoRuntime() {
|
|||||||
|
|
||||||
// Runtime entry point when using NativeActivity.
|
// Runtime entry point when using NativeActivity.
|
||||||
void ANativeActivity_onCreate(ANativeActivity *activity, void* savedState, size_t savedStateSize) {
|
void ANativeActivity_onCreate(ANativeActivity *activity, void* savedState, size_t savedStateSize) {
|
||||||
|
// Note that activity->clazz is mis-named.
|
||||||
current_vm = activity->vm;
|
current_vm = activity->vm;
|
||||||
|
current_ctx = (*activity->env)->NewGlobalRef(activity->env, activity->clazz);
|
||||||
|
current_native_activity = activity;
|
||||||
|
|
||||||
InitGoRuntime();
|
InitGoRuntime();
|
||||||
|
|
||||||
@ -121,7 +121,8 @@ void ANativeActivity_onCreate(ANativeActivity *activity, void* savedState, size_
|
|||||||
|
|
||||||
// Runtime entry point when embedding Go in a Java App.
|
// Runtime entry point when embedding Go in a Java App.
|
||||||
JNIEXPORT void JNICALL
|
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);
|
init_go_runtime(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,6 +34,12 @@ int go_started;
|
|||||||
// on android.
|
// on android.
|
||||||
JavaVM* current_vm;
|
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
|
// build_auxv builds an ELF auxiliary vector for initializing the Go
|
||||||
// runtime. While there does not appear to be any spec for this
|
// runtime. While there does not appear to be any spec for this
|
||||||
// format, there are some notes in
|
// format, there are some notes in
|
||||||
@ -166,10 +172,16 @@ func onConfigurationChanged(activity *C.ANativeActivity) {
|
|||||||
func onLowMemory(activity *C.ANativeActivity) {
|
func onLowMemory(activity *C.ANativeActivity) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// JavaInit is an initialization function registered by the package
|
type androidState struct {
|
||||||
// golang.org/x/mobile/bind/java. It gives the Java language
|
}
|
||||||
// bindings access to the JNI *JavaVM object.
|
|
||||||
var JavaInit func(javaVM uintptr)
|
func (androidState) JavaVM() unsafe.Pointer {
|
||||||
|
return unsafe.Pointer(C.current_vm)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (androidState) AndroidContext() unsafe.Pointer {
|
||||||
|
return unsafe.Pointer(C.current_ctx)
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
windowDestroyed = make(chan bool)
|
windowDestroyed = make(chan bool)
|
||||||
@ -227,6 +239,23 @@ func (a *asset) Close() error {
|
|||||||
return nil
|
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) {
|
func run(cb Callbacks) {
|
||||||
// We want to keep the event loop on a consistent OS thread.
|
// We want to keep the event loop on a consistent OS thread.
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
@ -237,20 +266,12 @@ func run(cb Callbacks) {
|
|||||||
C.free(unsafe.Pointer(ctag))
|
C.free(unsafe.Pointer(ctag))
|
||||||
C.free(unsafe.Pointer(cstr))
|
C.free(unsafe.Pointer(cstr))
|
||||||
|
|
||||||
if JavaInit != nil {
|
if C.current_native_activity == nil {
|
||||||
JavaInit(uintptr(unsafe.Pointer(C.current_vm)))
|
runStart(cb)
|
||||||
}
|
notifyInitDone()
|
||||||
|
select {}
|
||||||
// Inform Java that the program is initialized.
|
} else {
|
||||||
C.pthread_mutex_lock(&C.go_started_mu)
|
notifyInitDone()
|
||||||
C.go_started = 1
|
windowDrawLoop(cb, <-windowCreated, queue)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
22
app/app.go
22
app/app.go
@ -25,8 +25,12 @@ type Callbacks struct {
|
|||||||
// Start is called when the app enters the foreground.
|
// Start is called when the app enters the foreground.
|
||||||
// The app will start receiving Draw and Touch calls.
|
// The app will start receiving Draw and Touch calls.
|
||||||
//
|
//
|
||||||
// Window geometry will be configured and an OpenGL context
|
// If the app is responsible for the screen (that is, it is an
|
||||||
// will be available.
|
// 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
|
// Start is an equivalent lifecycle state to onStart() on
|
||||||
// Android and applicationDidBecomeActive on iOS.
|
// Android and applicationDidBecomeActive on iOS.
|
||||||
@ -82,3 +86,17 @@ type ReadSeekCloser interface {
|
|||||||
io.ReadSeeker
|
io.ReadSeeker
|
||||||
io.Closer
|
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.Width = geom.Pt(float32(C.windowWidth) / geom.PixelsPerPt)
|
||||||
geom.Height = geom.Pt(float32(C.windowHeight) / 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
|
// We start here rather than onStart so the window exists and the Gl
|
||||||
// context is configured.
|
// context is configured.
|
||||||
if cb.Start != nil {
|
if cb.Start != nil {
|
||||||
|
@ -8,11 +8,11 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"golang.org/x/mobile/app"
|
"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"
|
_ "golang.org/x/mobile/bind/java/testpkg/go_testpkg"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
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)
|
res.out = make(map[int32]*seq.Buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
// Init initializes communication with Java.
|
||||||
app.JavaInit = func(javaVM uintptr) {
|
// Typically called from the Start callback in app.Run.
|
||||||
C.init_seq(unsafe.Pointer(javaVM))
|
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) {
|
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
|
package main
|
||||||
|
|
||||||
import (
|
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() {
|
func main() {
|
||||||
app.Run(app.Callbacks{})
|
app.Run(app.Callbacks{Start: java.Init})
|
||||||
}
|
}
|
||||||
`))
|
`))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user