
最近有一个问题,是在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。
参考链接:
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)