bind/java: reenable asset access.

This is done by moving app.Context to internal/mobileinit,
introducing mobileinit.SetCurrentContext and,
making bind/java depend on it.

TODO: check gomobile bind's proguard rule - context lookup
was implemented through reflection on android.app.AppGlobals class.

Change-Id: Ieb6ad503eeef8c2c1c5836a21c667938c5a701a2
Reviewed-on: https://go-review.googlesource.com/12279
Reviewed-by: David Crawshaw <crawshaw@golang.org>
This commit is contained in:
Hyang-Ah (Hana) Kim 2015-07-16 00:23:53 -04:00 коммит произвёл Hyang-Ah Hana Kim
Родитель c6888a7bae
Коммит 136fa9bbbb
12 изменённых файлов: 131 добавлений и 39 удалений

Просмотреть файл

@ -45,8 +45,6 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
}
// Load classes here, which uses the correct ClassLoader.
current_vm = vm;
current_ctx = NULL;
current_ctx_clazz = find_class(env, "org/golang/app/GoNativeActivity");
current_ctx_clazz = (jclass)(*env)->NewGlobalRef(env, current_ctx_clazz);
@ -61,8 +59,10 @@ void ANativeActivity_onCreate(ANativeActivity *activity, void* savedState, size_
JNIEnv* env = activity->env;
// Note that activity->clazz is mis-named.
current_vm = activity->vm;
current_ctx = (*env)->NewGlobalRef(env, activity->clazz);
JavaVM* current_vm = activity->vm;
jobject current_ctx = activity->clazz;
setCurrentContext(current_vm, (*env)->NewGlobalRef(env, current_ctx));
// Set TMPDIR.
jmethodID gettmpdir = find_method(env, current_ctx_clazz, "getTmpdir", "()Ljava/lang/String;");

Просмотреть файл

@ -34,17 +34,6 @@ package app
#include <pthread.h>
#include <stdlib.h>
// current_vm is stored to initialize other cgo packages.
//
// As all the Go packages in a program form a single shared library,
// there can only be one JNI_OnLoad function for iniitialization. In
// OpenJDK there is JNI_GetCreatedJavaVMs, but this is not available
// on android.
JavaVM* current_vm;
// current_ctx is Android's android.context.Context. May be NULL.
jobject current_ctx;
jclass current_ctx_clazz;
jclass app_find_class(JNIEnv* env, const char* name);
@ -58,8 +47,14 @@ import (
"unsafe"
"golang.org/x/mobile/app/internal/callfn"
"golang.org/x/mobile/internal/mobileinit"
)
//export setCurrentContext
func setCurrentContext(vm *C.JavaVM, ctx C.jobject) {
mobileinit.SetCurrentContext(unsafe.Pointer(vm), unsafe.Pointer(ctx))
}
//export callMain
func callMain(mainPC uintptr) {
for _, name := range []string{"TMPDIR", "PATH", "LD_LIBRARY_PATH"} {
@ -186,25 +181,6 @@ func onConfigurationChanged(activity *C.ANativeActivity) {
func onLowMemory(activity *C.ANativeActivity) {
}
// Context holds global OS-specific context.
//
// Its extra methods are deliberately difficult to access because they must be
// used with care. Their use implies the use of cgo, which probably requires
// you understand the initialization process in the app package. Also care must
// be taken to write both Android, iOS, and desktop-testing versions to
// maintain portability.
type Context struct{}
// AndroidContext returns a jobject for the app android.context.Context.
func (Context) AndroidContext() unsafe.Pointer {
return unsafe.Pointer(C.current_ctx)
}
// JavaVM returns a JNI *JavaVM.
func (Context) JavaVM() unsafe.Pointer {
return unsafe.Pointer(C.current_vm)
}
var (
windowDestroyed = make(chan bool)
windowCreated = make(chan *C.ANativeWindow)

Просмотреть файл

@ -61,13 +61,13 @@ import (
"sync"
"unsafe"
"golang.org/x/mobile/app"
"golang.org/x/mobile/internal/mobileinit"
)
var assetOnce sync.Once
func assetInit() {
ctx := app.Context{}
ctx := mobileinit.Context{}
C.asset_manager_init(ctx.JavaVM(), ctx.AndroidContext())
}

Просмотреть файл

@ -4,6 +4,8 @@
package go;
import android.app.Application;
import android.content.Context;
import android.util.Log;
import android.util.SparseArray;
import android.util.SparseIntArray;
@ -24,6 +26,16 @@ public class Seq {
Log.w("GoSeq", "LoadJNI class not found");
}
try {
// TODO(hyangah): check proguard rule.
Application appl = (Application)Class.forName("android.app.AppGlobals").getMethod("getInitialApplication").invoke(null, (Object[]) null);
Context ctx = appl.getApplicationContext();
setContext(ctx);
} catch (Exception e) {
Log.w("GoSeq", "Global context not found:" + e);
}
initSeq();
new Thread("GoSeq") {
public void run() { Seq.receive(); }
@ -37,6 +49,8 @@ public class Seq {
ensure(64);
}
static native void setContext(Context ctx);
// Ensure that at least size bytes can be written to the Seq.
// Any existing data in the buffer is preserved.
public native void ensure(int size);

Просмотреть файл

@ -17,6 +17,12 @@ public class SeqTest extends AndroidTestCase {
public SeqTest() {
}
public void testAssets() {
String want = "Hello, Assets.\n";
String got = Testpkg.ReadAsset();
assertEquals("Asset read", want, got);
}
public void testAdd() {
long res = Testpkg.Add(3, 4);
assertEquals("Unexpected arithmetic failure", 7, res);

Просмотреть файл

@ -435,3 +435,12 @@ Java_go_Seq_recvRes(JNIEnv *env, jclass clazz, jint handle, jobject out_obj) {
}
RecvRes((int32_t)handle, out->buf, out->len);
}
JNIEXPORT void JNICALL
Java_go_Seq_setContext(JNIEnv* env, jclass clazz, jobject ctx) {
JavaVM* vm;
if ((*env)->GetJavaVM(env, &vm) != 0) {
LOG_FATAL("failed to get JavaVM");
}
setContext(vm, (*env)->NewGlobalRef(env, ctx));
}

Просмотреть файл

@ -6,6 +6,7 @@ package java // import "golang.org/x/mobile/bind/java"
//#cgo LDFLAGS: -llog
//#include <android/log.h>
//#include <jni.h>
//#include <stdint.h>
//#include <string.h>
//#include "seq_android.h"
@ -16,6 +17,7 @@ import (
"unsafe"
"golang.org/x/mobile/bind/seq"
"golang.org/x/mobile/internal/mobileinit"
)
const maxSliceLen = 1<<31 - 1
@ -178,3 +180,8 @@ func init() {
seq.EncString = encodeString
seq.DecString = decodeString
}
//export setContext
func setContext(vm *C.JavaVM, ctx C.jobject) {
mobileinit.SetCurrentContext(unsafe.Pointer(vm), unsafe.Pointer(ctx))
}

Просмотреть файл

@ -0,0 +1 @@
Hello, Assets.

Просмотреть файл

@ -11,8 +11,12 @@ package testpkg
import (
"errors"
"fmt"
"io/ioutil"
"log"
"runtime"
"time"
"golang.org/x/mobile/asset"
)
type I interface {
@ -156,3 +160,17 @@ func Hello(r Receiver, name string) {
func GarbageCollect() {
runtime.GC()
}
func ReadAsset() string {
rc, err := asset.Open("hello.txt")
if err != nil {
log.Fatal(err)
}
defer rc.Close()
b, err := ioutil.ReadAll(rc)
if err != nil {
log.Fatal(err)
}
return string(b)
}

Просмотреть файл

@ -198,7 +198,7 @@ import (
"log"
"unsafe"
"golang.org/x/mobile/app"
"golang.org/x/mobile/internal/mobileinit"
)
var (
@ -247,7 +247,7 @@ var (
)
func initAL() {
ctx := app.Context{}
ctx := mobileinit.Context{}
switch C.al_init(ctx.JavaVM(), ctx.AndroidContext(), &alHandle) {
case C.AL_INIT_RESULT_OK:
// No-op.

Просмотреть файл

@ -0,0 +1,61 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package mobileinit
/*
#include <jni.h>
#include <stdlib.h>
// current_vm is stored to initialize other cgo packages.
//
// As all the Go packages in a program form a single shared library,
// there can only be one JNI_OnLoad function for initialization. In
// OpenJDK there is JNI_GetCreatedJavaVMs, but this is not available
// on android.
JavaVM* current_vm;
// current_ctx is Android's android.context.Context. May be NULL.
jobject current_ctx;
// Set current_vm and current_ctx. The ctx passed in must be a global
// reference instance.
void set_vm_ctx(JavaVM* vm, jobject ctx) {
current_vm = vm;
current_ctx = ctx;
// TODO: check leak
}
*/
import "C"
import "unsafe"
// SetCurrentContext populates the global Context object with the specified
// current JavaVM instance (vm) and android.context.Context object (ctx).
// The android.context.Context object must be a global reference.
func SetCurrentContext(vm, ctx unsafe.Pointer) {
C.set_vm_ctx((*C.JavaVM)(vm), (C.jobject)(ctx))
}
// TODO(hyangah): should the app package have Context? It may be useful for
// external packages that need to access android context and vm.
// Context holds global OS-specific context.
//
// Its extra methods are deliberately difficult to access because they must be
// used with care. Their use implies the use of cgo, which probably requires
// you understand the initialization process in the app package. Also care must
// be taken to write both Android, iOS, and desktop-testing versions to
// maintain portability.
type Context struct{}
// AndroidContext returns a jobject for the app android.context.Context.
func (Context) AndroidContext() unsafe.Pointer {
return unsafe.Pointer(C.current_ctx)
}
// JavaVM returns a JNI *JavaVM.
func (Context) JavaVM() unsafe.Pointer {
return unsafe.Pointer(C.current_vm)
}

Просмотреть файл

@ -5,5 +5,5 @@
// Package mobileinit contains common initialization logic for mobile platforms
// that is relevant to both all-Go apps and gobind-based apps.
//
// Long-term, any code in this package should consider moving into Go stdlib.
// Long-term, some code in this package should consider moving into Go stdlib.
package mobileinit