Add dex example that dynamically loads a java class from a byte
stream.
This commit is contained in:
parent
dad071293c
commit
45f4e38ee3
6
.gitignore
vendored
6
.gitignore
vendored
|
@ -1,3 +1,9 @@
|
||||||
*.ast
|
*.ast
|
||||||
*.apk
|
*.apk
|
||||||
|
*.jar
|
||||||
|
*.dex
|
||||||
|
*.class
|
||||||
examples/sensors/sensors
|
examples/sensors/sensors
|
||||||
|
examples/jni/jni
|
||||||
|
examples/dex/dex
|
||||||
|
examples/permissions/permissions
|
||||||
|
|
237
examples/dex/bindata.go
Normal file
237
examples/dex/bindata.go
Normal file
|
@ -0,0 +1,237 @@
|
||||||
|
// Code generated by go-bindata.
|
||||||
|
// sources:
|
||||||
|
// java/classes.dex
|
||||||
|
// DO NOT EDIT!
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func bindataRead(data []byte, name string) ([]byte, error) {
|
||||||
|
gz, err := gzip.NewReader(bytes.NewBuffer(data))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Read %q: %v", name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
_, err = io.Copy(&buf, gz)
|
||||||
|
clErr := gz.Close()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Read %q: %v", name, err)
|
||||||
|
}
|
||||||
|
if clErr != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type asset struct {
|
||||||
|
bytes []byte
|
||||||
|
info os.FileInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
type bindataFileInfo struct {
|
||||||
|
name string
|
||||||
|
size int64
|
||||||
|
mode os.FileMode
|
||||||
|
modTime time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fi bindataFileInfo) Name() string {
|
||||||
|
return fi.name
|
||||||
|
}
|
||||||
|
func (fi bindataFileInfo) Size() int64 {
|
||||||
|
return fi.size
|
||||||
|
}
|
||||||
|
func (fi bindataFileInfo) Mode() os.FileMode {
|
||||||
|
return fi.mode
|
||||||
|
}
|
||||||
|
func (fi bindataFileInfo) ModTime() time.Time {
|
||||||
|
return fi.modTime
|
||||||
|
}
|
||||||
|
func (fi bindataFileInfo) IsDir() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
func (fi bindataFileInfo) Sys() interface{} {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _javaClassesDex = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x5c\x91\xbf\x4e\xc2\x50\x14\xc6\x7f\xa7\xe5\x4f\x22\x06\x11\x4d\x5c\x88\xe9\xa0\xab\x57\x23\x4e\x18\x13\xe3\x22\x09\xd1\x8d\xa5\x53\x15\x24\x25\x88\xc4\x56\x61\xd4\x84\x91\x47\xd0\xcd\x47\xf0\x11\x1c\x1c\x1c\xf4\x21\xdc\x58\xdc\x1d\xcc\xed\xbd\x40\xe3\x49\xda\xef\xe4\xf4\x9e\xef\x9c\xfe\x6e\xab\x3d\x5a\xda\xdd\x3f\xa0\x5e\xfd\x2c\xbf\xff\x3e\x7f\x7f\xbd\x6c\xbe\xf9\xcd\x1f\xff\xd6\x5f\x9f\xf8\x8d\xca\xf6\xb2\x03\x03\x60\xd4\xac\x96\xb1\x31\x16\xc8\x63\xea\x19\x60\x02\x38\xc0\x13\x8b\x70\x81\x57\x40\x80\x0f\xc0\x13\x98\x02\x15\x81\x2d\x81\x3d\x81\xaa\xc0\xa9\x40\x20\x70\x25\xc6\xc3\xb5\x9e\x39\xeb\x39\x8b\x9c\xfd\x46\xe2\x29\x89\x3a\x73\x85\xac\x55\xb1\x0f\x29\x8d\x64\xd1\xa7\xd3\x50\xcc\x8c\x41\x49\x57\x8b\x73\x9f\xbe\x98\x19\x6b\xac\xb2\xa2\x67\x1e\x86\xfd\x30\x3e\xa2\x70\x7c\xd2\x0b\xa2\x68\xa7\x1b\xdc\x07\x48\x9d\x72\x43\x67\xaa\x17\xf4\x3b\xea\xfc\xa2\xdb\xbe\x8c\x6b\x6c\x34\xa2\x58\x0d\x6f\x86\xaa\x13\xc6\xaa\xd5\x1e\x29\xd3\x53\xc3\x3d\xbb\xbb\x46\x9a\xb8\xe4\x8b\x64\xf5\x0b\x44\xe4\xf1\x21\x33\x15\x47\xc6\x7a\xe7\xc2\xbf\x7d\xb5\xce\xf8\x3a\x29\xc6\x6e\x8a\x73\x36\xc5\x38\x97\xe2\x2c\x9e\x39\xa3\x59\x3b\x9e\xf1\xd1\xcc\x5d\x5b\xd7\xff\x8e\x67\xce\x27\x5c\x4a\x26\xd7\x77\xfa\x17\x00\x00\xff\xff\xe4\x6b\xad\x5e\x0c\x02\x00\x00")
|
||||||
|
|
||||||
|
func javaClassesDexBytes() ([]byte, error) {
|
||||||
|
return bindataRead(
|
||||||
|
_javaClassesDex,
|
||||||
|
"java/classes.dex",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func javaClassesDex() (*asset, error) {
|
||||||
|
bytes, err := javaClassesDexBytes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
info := bindataFileInfo{name: "java/classes.dex", size: 524, mode: os.FileMode(420), modTime: time.Unix(1572550715, 0)}
|
||||||
|
a := &asset{bytes: bytes, info: info}
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Asset loads and returns the asset for the given name.
|
||||||
|
// It returns an error if the asset could not be found or
|
||||||
|
// could not be loaded.
|
||||||
|
func Asset(name string) ([]byte, error) {
|
||||||
|
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||||
|
if f, ok := _bindata[cannonicalName]; ok {
|
||||||
|
a, err := f()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
|
||||||
|
}
|
||||||
|
return a.bytes, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("Asset %s not found", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustAsset is like Asset but panics when Asset would return an error.
|
||||||
|
// It simplifies safe initialization of global variables.
|
||||||
|
func MustAsset(name string) []byte {
|
||||||
|
a, err := Asset(name)
|
||||||
|
if err != nil {
|
||||||
|
panic("asset: Asset(" + name + "): " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssetInfo loads and returns the asset info for the given name.
|
||||||
|
// It returns an error if the asset could not be found or
|
||||||
|
// could not be loaded.
|
||||||
|
func AssetInfo(name string) (os.FileInfo, error) {
|
||||||
|
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||||
|
if f, ok := _bindata[cannonicalName]; ok {
|
||||||
|
a, err := f()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
|
||||||
|
}
|
||||||
|
return a.info, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("AssetInfo %s not found", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssetNames returns the names of the assets.
|
||||||
|
func AssetNames() []string {
|
||||||
|
names := make([]string, 0, len(_bindata))
|
||||||
|
for name := range _bindata {
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
|
||||||
|
// _bindata is a table, holding each asset generator, mapped to its name.
|
||||||
|
var _bindata = map[string]func() (*asset, error){
|
||||||
|
"java/classes.dex": javaClassesDex,
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssetDir returns the file names below a certain
|
||||||
|
// directory embedded in the file by go-bindata.
|
||||||
|
// For example if you run go-bindata on data/... and data contains the
|
||||||
|
// following hierarchy:
|
||||||
|
// data/
|
||||||
|
// foo.txt
|
||||||
|
// img/
|
||||||
|
// a.png
|
||||||
|
// b.png
|
||||||
|
// then AssetDir("data") would return []string{"foo.txt", "img"}
|
||||||
|
// AssetDir("data/img") would return []string{"a.png", "b.png"}
|
||||||
|
// AssetDir("foo.txt") and AssetDir("notexist") would return an error
|
||||||
|
// AssetDir("") will return []string{"data"}.
|
||||||
|
func AssetDir(name string) ([]string, error) {
|
||||||
|
node := _bintree
|
||||||
|
if len(name) != 0 {
|
||||||
|
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||||
|
pathList := strings.Split(cannonicalName, "/")
|
||||||
|
for _, p := range pathList {
|
||||||
|
node = node.Children[p]
|
||||||
|
if node == nil {
|
||||||
|
return nil, fmt.Errorf("Asset %s not found", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if node.Func != nil {
|
||||||
|
return nil, fmt.Errorf("Asset %s not found", name)
|
||||||
|
}
|
||||||
|
rv := make([]string, 0, len(node.Children))
|
||||||
|
for childName := range node.Children {
|
||||||
|
rv = append(rv, childName)
|
||||||
|
}
|
||||||
|
return rv, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type bintree struct {
|
||||||
|
Func func() (*asset, error)
|
||||||
|
Children map[string]*bintree
|
||||||
|
}
|
||||||
|
var _bintree = &bintree{nil, map[string]*bintree{
|
||||||
|
"java": &bintree{nil, map[string]*bintree{
|
||||||
|
"classes.dex": &bintree{javaClassesDex, map[string]*bintree{}},
|
||||||
|
}},
|
||||||
|
}}
|
||||||
|
|
||||||
|
// RestoreAsset restores an asset under the given directory
|
||||||
|
func RestoreAsset(dir, name string) error {
|
||||||
|
data, err := Asset(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
info, err := AssetInfo(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RestoreAssets restores an asset under the given directory recursively
|
||||||
|
func RestoreAssets(dir, name string) error {
|
||||||
|
children, err := AssetDir(name)
|
||||||
|
// File
|
||||||
|
if err != nil {
|
||||||
|
return RestoreAsset(dir, name)
|
||||||
|
}
|
||||||
|
// Dir
|
||||||
|
for _, child := range children {
|
||||||
|
err = RestoreAssets(dir, filepath.Join(name, child))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func _filePath(dir, name string) string {
|
||||||
|
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||||
|
return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
|
||||||
|
}
|
||||||
|
|
206
examples/dex/dex_android.go
Normal file
206
examples/dex/dex_android.go
Normal file
|
@ -0,0 +1,206 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo LDFLAGS: -landroid -llog
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <jni.h>
|
||||||
|
#include <android/log.h>
|
||||||
|
|
||||||
|
static jobject gClassLoader;
|
||||||
|
static jmethodID gFindClassMethod;
|
||||||
|
|
||||||
|
jobject
|
||||||
|
CreateObject(JNIEnv* env, jclass cls) {
|
||||||
|
jmethodID init = (*env)->GetMethodID(env, cls, "<init>", "()V");
|
||||||
|
return (*env)->NewObject(env, cls, init);
|
||||||
|
}
|
||||||
|
|
||||||
|
jmethodID GetMethodID(JNIEnv *env, jclass cls, const char *name, const char *sig) {
|
||||||
|
return (*env)->GetMethodID(env, cls, name, sig);
|
||||||
|
}
|
||||||
|
|
||||||
|
jclass
|
||||||
|
FindClass(JNIEnv* env, char* name) {
|
||||||
|
jstring strClassName = (*env)->NewStringUTF(env, name);
|
||||||
|
return (*env)->CallObjectMethod(env, gClassLoader, gFindClassMethod, strClassName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SetupJVM(JNIEnv* env, jobject context, void* buf, int len) {
|
||||||
|
jclass cls = (*env)->GetObjectClass(env, context);
|
||||||
|
jmethodID mid = (*env)->GetMethodID(env, cls, "getClassLoader", "()Ljava/lang/ClassLoader;");
|
||||||
|
jobject loader = (*env)->CallObjectMethod(env, context, mid);
|
||||||
|
gClassLoader = (*env)->NewGlobalRef(env, loader);
|
||||||
|
cls = (*env)->GetObjectClass(env, loader);
|
||||||
|
gFindClassMethod = (*env)->GetMethodID(env, cls, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;");
|
||||||
|
|
||||||
|
__android_log_write(ANDROID_LOG_DEBUG, "gio", "Looking for InMemoryDexClassLoader");
|
||||||
|
cls = (*env)->FindClass(env, "dalvik/system/InMemoryDexClassLoader");
|
||||||
|
if (cls == NULL) {
|
||||||
|
__android_log_write(ANDROID_LOG_DEBUG, "gio", "not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
__android_log_write(ANDROID_LOG_DEBUG, "gio", "NewDirectByteBuffer");
|
||||||
|
jobject bbuf = (*env)->NewDirectByteBuffer(env, buf, (jlong)len);
|
||||||
|
if (bbuf == NULL) {
|
||||||
|
__android_log_write(ANDROID_LOG_DEBUG, "gio", "failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
__android_log_write(ANDROID_LOG_DEBUG, "gio", "looking for <init>");
|
||||||
|
mid = (*env)->GetMethodID(env, cls, "<init>", "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V");
|
||||||
|
if (mid == NULL) {
|
||||||
|
__android_log_write(ANDROID_LOG_DEBUG, "gio", "not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
__android_log_write(ANDROID_LOG_DEBUG, "gio", "calling <init>");
|
||||||
|
jobject dexLoader = (*env)->NewObject(env, cls, mid, bbuf, gClassLoader);
|
||||||
|
// make the dexLoader our new class loader
|
||||||
|
gClassLoader = (*env)->NewGlobalRef(env, dexLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
// __android_log_write(ANDROID_LOG_DEBUG, "gio", "looking for AClass");
|
||||||
|
// cls = FindClass(env, "st/wow/git/dex/AClass");
|
||||||
|
// if (cls == NULL) {
|
||||||
|
// __android_log_write(ANDROID_LOG_DEBUG, "gio", "not found");
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// __android_log_write(ANDROID_LOG_DEBUG, "gio", "calling CreateObject");
|
||||||
|
// jobject obj = CreateObject(env, cls);
|
||||||
|
// if (obj == NULL) {
|
||||||
|
// __android_log_write(ANDROID_LOG_DEBUG, "gio", "failed");
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// __android_log_write(ANDROID_LOG_DEBUG, "gio", "looking for <init>");
|
||||||
|
// mid = (*env)->GetMethodID(env, cls, "<init>", "()V");
|
||||||
|
// if (mid == NULL) {
|
||||||
|
// __android_log_write(ANDROID_LOG_DEBUG, "gio", "not found");
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// __android_log_write(ANDROID_LOG_DEBUG, "gio", "creating object");
|
||||||
|
// obj = (*env)->NewObject(env, cls, mid);
|
||||||
|
// if (obj == NULL) {
|
||||||
|
// __android_log_write(ANDROID_LOG_DEBUG, "gio", "failed");
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// __android_log_write(ANDROID_LOG_DEBUG, "gio", "looking for Num");
|
||||||
|
// mid = (*env)->GetMethodID(env, cls, "Num", "()I");
|
||||||
|
// if (mid == NULL) {
|
||||||
|
// __android_log_write(ANDROID_LOG_DEBUG, "gio", "not found");
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// jint val = (*env)->CallIntMethod(env, obj, mid);
|
||||||
|
// __android_log_print(ANDROID_LOG_DEBUG, "gio", "value = %d", val);
|
||||||
|
|
||||||
|
|
||||||
|
jint
|
||||||
|
CallIntMethod(JNIEnv *env, jobject obj, jmethodID methodID) {
|
||||||
|
return (*env)->CallIntMethod(env, obj, methodID);
|
||||||
|
}
|
||||||
|
|
||||||
|
jint
|
||||||
|
GetEnv(JavaVM *vm, JNIEnv **env, jint version) {
|
||||||
|
return (*vm)->GetEnv(vm, (void **)env, version);
|
||||||
|
}
|
||||||
|
|
||||||
|
jint
|
||||||
|
AttachCurrentThread(JavaVM *vm, JNIEnv **p_env, void *thr_args) {
|
||||||
|
return (*vm)->AttachCurrentThread(vm, p_env, thr_args);
|
||||||
|
}
|
||||||
|
|
||||||
|
jint
|
||||||
|
DetachCurrentThread(JavaVM *vm) {
|
||||||
|
return (*vm)->DetachCurrentThread(vm);
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"gioui.org/app"
|
||||||
|
)
|
||||||
|
|
||||||
|
var theJVM *C.JavaVM
|
||||||
|
|
||||||
|
func SetJVM() {
|
||||||
|
h := app.PlatformHandle()
|
||||||
|
theJVM = (*C.JavaVM)(unsafe.Pointer(h.JVM))
|
||||||
|
dex := MustAsset("java/classes.dex")
|
||||||
|
RunInJVM(func(env *C.JNIEnv) {
|
||||||
|
labchan <- "Set up JVM"
|
||||||
|
C.SetupJVM(
|
||||||
|
env,
|
||||||
|
(C.jobject)(h.Context),
|
||||||
|
unsafe.Pointer(&dex[0]),
|
||||||
|
C.int(len(dex)),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func CallJNI() {
|
||||||
|
labchan <- "Call JNI"
|
||||||
|
RunInJVM(func(env *C.JNIEnv) {
|
||||||
|
labchan <- "FindClass"
|
||||||
|
cls := JniFindClass(env, "st/wow/git/dex/AClass")
|
||||||
|
labchan <- "GetMethodID"
|
||||||
|
mid := JniGetMethodID(env, cls, "Num", "()I")
|
||||||
|
labchan <- "CreateObject"
|
||||||
|
obj := JniCreateObject(env, cls)
|
||||||
|
val := JniCallIntMethod(env, obj, mid)
|
||||||
|
labchan <- fmt.Sprintf("val = %d", val)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func JniFindClass(env *C.JNIEnv, name string) C.jclass {
|
||||||
|
cname := C.CString(name)
|
||||||
|
defer C.free(unsafe.Pointer(cname))
|
||||||
|
return C.FindClass((*C.JNIEnv)(env), cname)
|
||||||
|
}
|
||||||
|
|
||||||
|
func JniCreateObject(env *C.JNIEnv, cls C.jclass) C.jobject {
|
||||||
|
return C.CreateObject(env, (C.jclass)(cls))
|
||||||
|
}
|
||||||
|
|
||||||
|
func JniCallIntMethod(env *C.JNIEnv, obj C.jobject, methodID C.jmethodID) int {
|
||||||
|
return (int)(C.CallIntMethod(env, obj, methodID))
|
||||||
|
}
|
||||||
|
|
||||||
|
func JniGetMethodID(env *C.JNIEnv, cls C.jclass, name, sig string) C.jmethodID {
|
||||||
|
cname := C.CString(name)
|
||||||
|
defer C.free(unsafe.Pointer(cname))
|
||||||
|
csig := C.CString(sig)
|
||||||
|
defer C.free(unsafe.Pointer(csig))
|
||||||
|
return C.GetMethodID(env, cls, cname, csig)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunInJVM(f func(env *C.JNIEnv)) {
|
||||||
|
runtime.LockOSThread()
|
||||||
|
defer runtime.UnlockOSThread()
|
||||||
|
var env *C.JNIEnv
|
||||||
|
var detach bool
|
||||||
|
if res := C.GetEnv(theJVM, &env, C.JNI_VERSION_1_6); res != C.JNI_OK {
|
||||||
|
if res != C.JNI_EDETACHED {
|
||||||
|
panic(fmt.Errorf("JNI GetEnv failed with error %d", res))
|
||||||
|
}
|
||||||
|
if C.AttachCurrentThread(theJVM, &env, nil) != C.JNI_OK {
|
||||||
|
panic(errors.New("runInJVM: AttachCurrentThread failed"))
|
||||||
|
}
|
||||||
|
detach = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if detach {
|
||||||
|
defer func() {
|
||||||
|
C.DetachCurrentThread(theJVM)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
f(env)
|
||||||
|
}
|
9
examples/dex/dex_other.go
Normal file
9
examples/dex/dex_other.go
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
func SetJVM() {
|
||||||
|
dex := MustAsset("java/classes.dex")
|
||||||
|
_ = dex
|
||||||
|
}
|
||||||
|
|
||||||
|
func CallJNI() {
|
||||||
|
}
|
9
examples/dex/java/AClass.java
Normal file
9
examples/dex/java/AClass.java
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
package st.wow.git.dex;
|
||||||
|
|
||||||
|
public class AClass {
|
||||||
|
public int Num() {
|
||||||
|
return 17;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
5
examples/dex/java/gen.go
Normal file
5
examples/dex/java/gen.go
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
//go:generate javac -bootclasspath $ANDROID_HOME/platforms/android-29/android.jar AClass.java
|
||||||
|
//go:generate $ANDROID_HOME/build-tools/28.0.3/dx --dex --output=classes.dex --no-strict AClass.class
|
||||||
|
//go:generate rm AClass.class
|
||||||
|
|
||||||
|
package java
|
70
examples/dex/main.go
Normal file
70
examples/dex/main.go
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
//go:generate go generate ./java
|
||||||
|
//go:generate go-bindata java/classes.dex
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gioui.org/app"
|
||||||
|
"gioui.org/layout"
|
||||||
|
"gioui.org/io/system"
|
||||||
|
"gioui.org/unit"
|
||||||
|
"gioui.org/widget/material"
|
||||||
|
|
||||||
|
"gioui.org/font/gofont"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
labchan chan string
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
labchan = make(chan string)
|
||||||
|
go eventloop()
|
||||||
|
app.Main()
|
||||||
|
}
|
||||||
|
|
||||||
|
func eventloop() {
|
||||||
|
gofont.Register()
|
||||||
|
w := app.NewWindow(app.Title("Dex"))
|
||||||
|
th := material.NewTheme()
|
||||||
|
gtx := &layout.Context{Queue: w.Queue()}
|
||||||
|
|
||||||
|
sysinset := &layout.Inset{}
|
||||||
|
margin := layout.UniformInset(unit.Dp(10))
|
||||||
|
labels := []material.Label{}
|
||||||
|
list := &layout.List{Axis: layout.Vertical}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
labchan <- "Starting"
|
||||||
|
SetJVM()
|
||||||
|
CallJNI()
|
||||||
|
}()
|
||||||
|
|
||||||
|
resetSysinset := func(x system.Insets) {
|
||||||
|
sysinset.Top = x.Top
|
||||||
|
sysinset.Bottom = x.Bottom
|
||||||
|
sysinset.Left = x.Left
|
||||||
|
sysinset.Right = x.Right
|
||||||
|
}
|
||||||
|
|
||||||
|
for { select {
|
||||||
|
case x := <-labchan:
|
||||||
|
labels = append(labels, th.Body1(x))
|
||||||
|
case e := <-w.Events():
|
||||||
|
switch e := e.(type) {
|
||||||
|
case system.DestroyEvent:
|
||||||
|
return
|
||||||
|
case system.FrameEvent:
|
||||||
|
gtx.Reset(e.Config, e.Size)
|
||||||
|
resetSysinset(e.Insets)
|
||||||
|
sysinset.Layout(gtx, func() {
|
||||||
|
margin.Layout(gtx, func() {
|
||||||
|
list.Layout(gtx, len(labels), func(i int) {
|
||||||
|
labels[i].Layout(gtx)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
e.Frame(gtx.Ops)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
}
|
|
@ -26,13 +26,6 @@ func main() {
|
||||||
log.Print("app.Main() returned")
|
log.Print("app.Main() returned")
|
||||||
}
|
}
|
||||||
|
|
||||||
func diffInsets(x, y system.Insets) bool {
|
|
||||||
return x.Top != y.Top ||
|
|
||||||
x.Bottom != y.Bottom ||
|
|
||||||
x.Left != y.Left ||
|
|
||||||
x.Right != y.Right
|
|
||||||
}
|
|
||||||
|
|
||||||
func eventloop() {
|
func eventloop() {
|
||||||
w := app.NewWindow(
|
w := app.NewWindow(
|
||||||
app.Size(unit.Dp(400), unit.Dp(400)),
|
app.Size(unit.Dp(400), unit.Dp(400)),
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
"gioui.org/unit"
|
"gioui.org/unit"
|
||||||
"gioui.org/widget/material"
|
"gioui.org/widget/material"
|
||||||
|
|
||||||
_ "gioui.org/font/gofont"
|
"gioui.org/font/gofont"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -34,6 +34,7 @@ func diffInsets(x, y system.Insets) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func eventloop() {
|
func eventloop() {
|
||||||
|
gofont.Register()
|
||||||
w := app.NewWindow(
|
w := app.NewWindow(
|
||||||
app.Size(unit.Dp(400), unit.Dp(400)),
|
app.Size(unit.Dp(400), unit.Dp(400)),
|
||||||
app.Title("Hello"))
|
app.Title("Hello"))
|
||||||
|
|
Loading…
Reference in New Issue
Block a user