golang调用java

golang调用java,第1张

最近有一个问题,是在golang作为底层,想获取apk的版本号和版本号代码(通俗一点就是versionName和versionCode),如果这时java代码,可以很容易地通过

// please add try/catch ... ;-)

PackageInfo info = context.getPackageManager().getPackageInfo(getPackageName(), 0);

String versionName = info.versionName;

int versionCode = info.versionCode;

来获取,但是,如果你的主要程序都是go语言来写,需要在go语言中获取apk的版本号怎么处理呢?

主要的解决思路有三种:

一是在你的go程序初始化的时候,通过java上层传参的形式,将这些参数传递下来

二是通过gobind来直接调用java代码

三是通过cgo来写ndk代码,调用java方法

解决思路的分析

第一种java传参给go语言入口函数

优势在于写起来非常简单,只需要go语言的接口加参数即可,然后java调用go语言代码的时候加入参数即可,缺点就是以后有新需求来的时候,还需要加参数,可能参数会越来越长…

第二种通过gobind来直接调用java代码

gobind的官方代码有这么一段:

一开始我以为我也可以在上层直接将Java代码写好,context什么的直接通过重写Appliction类,得到一个static的context,就可以在go语言中直接使用啦,心里美滋滋的…

但是,直到我将代码都写好了之后,才发现,原来gobind并不支持import自己写的类,而是已知的Android API而已…

因此,这种思路尝试到了最后不得不放弃了

第三种通过cgo来调用java代码

可以说,这个方法算是不得以而为之吧,我也是看到了go语言针对asset资源目录的处理方式才有所顿悟:

有兴趣的自己翻来看一下,具体路径$GOPATH/src/golang.org/x/mobile/asset/asset_android.go。

虽然比较难看,但写起来还是那么酷酷的。

优点是,不需要去动java上层的代码,直接在cgo里边完成需求

缺点是,相对难以理解,对于不了解jni *** 作的人来说维护起来相对困难,调试也不太方便

示例:获取版本号

/*

#cgo LDFLAGS: -landroid

#include

#include

#include

int versionCode = 0;

char versionName[16] = "";

const char* pVersionName = NULL;

int getVersion(uintptr_t java_vm, uintptr_t jni_env, jobject ctx)

{

JavaVM *vm = (JavaVM *)java_vm;

JNIEnv *env = (JNIEnv *)jni_env;

jclass ctxClazz = (*env)->FindClass(env, "android/content/Context");

jstring packageName;

jobject packageManagerObj;

jobject packageInfoObj;

jmethodID getPackageName = (*env)->GetMethodID(env, ctxClazz, "getPackageName", "()Ljava/lang/String;");

jmethodID getPackageManager = (*env)->GetMethodID(env, ctxClazz, "getPackageManager", "()Landroid/content/pm/PackageManager;");

jclass pmClazz = (*env)->FindClass(env, "android/content/pm/PackageManager");

jmethodID getPackageInfo = (*env)->GetMethodID(env, pmClazz, "getPackageInfo", "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");

jclass packageInfoClass = (*env)->FindClass(env, "android/content/pm/PackageInfo");

jfieldID versionCodeFid = (*env)->GetFieldID(env, packageInfoClass, "versionCode", "I");

jfieldID versionNameFid = (*env)->GetFieldID(env, packageInfoClass, "versionName", "Ljava/lang/String;");

packageName = (jstring)(*env)->CallObjectMethod(env, ctx, getPackageName);

packageManagerObj = (*env)->CallObjectMethod(env, ctx, getPackageManager);

packageInfoObj = (*env)->CallObjectMethod(env, packageManagerObj, getPackageInfo, packageName, 0x0);

// version code

versionCode = (*env)->GetIntField(env, packageInfoObj, versionCodeFid);

// version name

jstring jstr = (*env)->GetObjectField(env, packageInfoObj, versionNameFid);

const char *str = (*env)->GetStringUTFChars(env, jstr, NULL);

strcpy(versionName, str);

pVersionName = versionName;

(*env)->ReleaseStringUTFChars(env, jstr, str);

return versionCode;

}

*/

import "C"

var getVersionOnce sync.Once

func getVersionJni() {

e := app.RunOnJVM(func(vm, env, ctx uintptr) error {

C.getVersion(C.uintptr_t(vm), C.uintptr_t(env), C.jobject(ctx))

return nil

})

if e != nil {

log.Fatalf("getVersionJni: fatal: %s", e.Error())

}

}

func GetVersion() (v string) {

getVersionOnce.Do(getVersionJni)

log.Printf("GetVersion: versionCode: %d, versionName: %s", C.versionCode, C.GoString(C.pVersionName))

return strings.TrimSpace(C.GoString(C.pVersionName))

}

这里有一个技巧,由于versionName和versionCode几乎是在运行过程不会被修改的,所以使用sync.Once来确保getVersionJni函数只会被执行了一次,后续的直接从结果里边取出来就可以了。

提示:app.RunOnJVM如果找不到方法,你可能需要升级一下gomobile。

参考链接:

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/langs/996366.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-05-21
下一篇2022-05-21

发表评论

登录后才能评论

评论列表(0条)

    保存