Android调用go语言 - 详细版

Posted 大雄童鞋

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android调用go语言 - 详细版相关的知识,希望对你有一定的参考价值。

android调用go语言就一句话:Android Studio 引入go代码打包的aar进行代码调用
准备工作:

Android 方面:
1、JDK环境
2、Android SDK
3、Android NDK

Go方面:
1、Golang环境
2、gomobile安装

Android方面JDK,SDK,NDK我就不说了,注意环境变量配置的时候,不要出错(比如空格)
安装好后,dos中分别执行 java;javac;java -version 来验证JDK环境配置成功与否,避免后面打包aar出问题。
SDK和NDK基本不需要怎么配置,指定路径即可。

go方面Golang的安装也很简单,下载安装并配置环境变量(安装的时候会自动配置好)即可
gomobile由于翻墙问题,我们就自己配置了。

具体操作如下(本人使用的是Windows10,此教程也是win10环境下的):

首先先搭建Android环境:

环境变量:(%JAVA_HOME%代表引用JAVA_HOME的地址)

JAVA_HOME : E:\\Java\\jdk1.8.0_161
CLASSPATH : .;%JAVA_HOME%\\lib;%JAVA_HOME%\\lib\\tools.jar
PATH:       %JAVA_HOME%\\bin;%JAVA_HOME%\\jre\\bin;
  • Android SDK + Android NDK
    建议直接下载Android Studio 进行在线安装(File - > Setting - > Android SDK)

环境变量:
--- Android SDK ---
ANDROID_HOME: E:\\Android\\SDK
PATH:         %ANDROID_HOME%\\platform-tools;%ANDROID_HOME%\\tools;

--- Android NDK ---
NDK_ROOT:E:\\Android\\SDK\\ndk-bundle
PATH:%NDK_ROOT%;

然后搭建Go环境:

  • Golang下载安装(我安装在F盘Go文件夹下)
环境变量(安装后这些环境变量有些会自动配置好,检查下即可)

GOROOT: F:\\Go\\
 GOBIN: F:\\Go\\bin
GOPATH: F:\\goWorks
  • gomobile
    自己手动配置:

1、下载go仓库的mobile项目
2、执行以下命令:

$ mkdir -p $GOPATH/src/github.com/golang 
$ cd $GOPATH/src/github.com/golang 
$ git clone https://github.com/golang/mobile.git 
$ mkdir -p $GOPATH/src/golang.org/x 
$ cp -r $GOPATH/src/github.com/golang/mobile $GOPATH/src/golang.org/x 
cd $GOPATH/src/golang.org/x/mobile/cmd/gobind
$ go install //会在$GOPATH/bin 目录下生成gobind
$ cd $GOPATH/src/golang.org/x/mobile/cmd/gomobile
$ go install //会在$GOPATH/bin 目录下生成gomobile
$ gomobile init
gomobile init -ndk /usr/local/androidNDK/android-ndk-r20

由于这些命令是Linux命令,我们在Windows下,我就直接根据顺序手动操作了。
之前在安装Golang的时候指定了一个GOPATH: F:\\goWorks 路径,上面的$GOPATH 就是指我们这个路径。

(src/github.com/golang 即$GOPATH下新建src,src下新建github.com,github.com下新建golang)
  • 在$GOPATH下新建 文件夹src/github.com/golang
  • Dos中切换到src/github.com/golang路径下(cd F:\\goWorks\\src\\github.com\\golang)
  • 通过git下载mobile (git clone https://github.com/golang/mobile.git),之前下载过,复制进来即可
  • 在$GOPATH下新建文件夹/src/golang.org/x
  • 将mobile文件拷贝到/src/golang.org/x 下
  • Dos中切换到gomobile下(cd F:\\goWorks\\src\\golang.org\\x\\mobile\\cmd\\gomobile)
  • 执行go install //会在$GOPATH/src/bin 目录下生成gomobile.exe
  • Dos中切换到gomobile下(cd F:\\goWorks\\src\\golang.org\\x\\mobile\\cmd\\gobind)
  • 执行go install //会在$GOPATH/src/bin 目录下生成gobind.exe
  • gomobile init -ndk D:\\Android\\SDK\\ndk-bundle

-----------------------------更新-----------------------------

  • 路径更新

2018.5.4更新,今天看到mobile项目在3天前(即2018.5.1)更新了一个版本,所以我就重新下载了试试新版本的功能,发现执行了go build 和 go install 后,生成的gobind.exe 和gomobile.exe转移到了 F:\\Go\\bin目录下
同时aar包和jar包也转移到了 F:\\Go\\bin目录下,而不是之前的$GOPATH/src/bin目录。

  • 支持的基本数据类型

经过测试,Java支持的基本类型有:float32 ,float64, uint8
不支持的类型有:uint,uint32,uint64
更多类型待更新。。。

初始化结束后是这样的:


这样,gomobile的配置就结束了。

下面你就可以编写Golang代码进行编译打包aar或者打包apk了。

其实我们下载的mobile项目中本身就有一些demo,可以直接使用的在mobile/example下

打包:首先cd 到 F:\\goWorks\\bin 目录,因为bin目录下有gobind.exe 和gomobile.exe,所以可以执行下面操作

--- 打包apk ---
gomobile build -target=android golang.org/x/mobile/example/basic
--- 打包aar ---
1、将example下的bind中的hello文件夹复制到src下面
2、gomobile bind -target=android hello

--------------------------------附加(真实项目实战):------------------------------

//cd 到 F:\\goWorks\\bin 
//打aar包:gomobile bind -target=android github.com\\zx\\largezx
//其中largezx文件夹下要有一个largezx.go (go文件的名称要和文件夹一样)
//打包成功


将example下的bind中的hello文件夹复制到src下面,如图

打包之后就会在bin下生成aar文件或apk文件

最后就是Android Studio 里面的调用了。

1、把aar包放入libs中

2、配置app文件夹下的build.gradle文件

完整如下:




apply plugin: 'com.android.application'

android 
    compileSdkVersion 26
    defaultConfig 
        applicationId "com.fzm.go"
        minSdkVersion 15
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        externalNativeBuild 
            cmake 
                cppFlags ""
            
        
    
    buildTypes 
        release 
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        
    
    externalNativeBuild 
        cmake 
            path "CMakeLists.txt"
        
    

repositories 
    flatDir 
        dirs 'libs'
    

dependencies 
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:26.1.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
    implementation(name: 'hello', ext: 'aar')


配置完后,就可以在External Libraries 中看到了。

客户端调用:

效果:

附加问题库:

问题一:

最开始我们引用了2个aar(hello 和largezx),两个项目引用同一个库可能有冲突

dependencies 
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:26.1.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
    //implementation(name: 'hello', ext: 'aar')
    implementation(name: 'largezx', ext: 'aar')

如上,那么运行的时候就出错了:
错误如下:

Error:Execution failed for task ':app:transformNativeLibsWithMergeJniLibsForDebug'.
> More than one file was found with OS independent path 'lib/x86/libgojni.so'

问题二:打包出的aar包中go的方法不全

因为你执行gomobile bind -target=android github.com\\zx\\largezx 这个操作打包的时候会打出2个包:
aar包和jar包,如:largezx.aar和largezx-sources.jar,当出现这个问题后,我去代码里面找相关的方法,发现无法编译成功的方法中都有一个类型: uint32,我就猜测是不是java不支持这个类型。

然后我查了查largezx-sources.jar里面的这个类发现了一行一行的错误:即
skipped const TypeAIB with unsupported type: uint32(不支持类型的跳过const类型:uTI32)
意思是:Java进行编译go语言的时候,不支持该类型,所以跳过编译操作了,那么打包里面肯定调用不了了。
编译源代码如下:
我们看jar包中的largezx.java文件:

// Java class bipwallet.Bipwallet is a proxy for talking to a Go program.
//   gobind -lang=java gitlab.33.cn/wallet/bipwallet
//
// File is generated by gobind. Do not edit.
package largezx;

import go.Seq;

public abstract class largezx 
    static 
        Seq.touch(); // for loading the native library
        _init();
    
    
    private Largezx()  // uninstantiable
    
    // touch is called from other bound packages to initialize this package
    public static void touch() 
    
    private static native void _init();
    
    
    // skipped const Types with unsupported type: uint32
    
    // skipped function method1 with unsupported parameter or return types
    
    // skipped function methodAddress with unsupported parameter or return types
    


可以发现跳过了好多类型,另外测试了下uint, uint64等都不支持,解决办法是让go开发人员把方法包装一下,入参和返回参数都设置成常用类型:string, int等。

问题三:go语言返回值可以有多个,但是Java只能有1个返回值,所以go语言想要给Java编译调用,必须要避免多返回值的情况

不然就会报如下错:

functions and methods must return either zero or one values, and optionally an error

下面看看多个返回值的方法:

func TestMehtod(index int) (priv, pub []byte, err error) 
	return priv, pub, err

解读下:入参:index(int类型),返回值 priv,pub,err 

上面的写法等同于:
func TestMehtod(index int) (priv []byte, pub []byte, err error) 
	return priv, pub, err

解决办法:拆分go方法,避免多返回类型

拓展:该方法需要指定对象调用,什么意思?
如下代码:

func (w *HDWallet) TestMehtod(index int) (pub []byte, err error) 
	return pub, err

那么怎么调用TestMehtod呢?
调用如下:
pub, err := wallet.TestMehtod(index)   (即想调用该方法必须先拿到wallet对象,用wallet进行调用。)





那么怎么解决呢?
很简单,我们提供一个方法,先拿到wallet,

最后总结一下:

1、不能使用java不支持的数据类型,比如uint32
2、不能使用多返回值
3、Android引用多个aar包注意资源重复

附加在Android Sudio中编写go代码并编译调用和go语言语法的熟悉

1、用Go语言写Android应用 (1) - 用Go写本地应用
2、用Go语言写Android应用 (2) - 从Android的Java调用Go代码
3、用Go写Android应用(3) - Go语言速成

附加:可以在工具里面直接打包


也可以直接在walletapi下面执行:gomobile bind -target=android即可。

新增注意点:

gomobile bind -target=android gitlab\\wallet\\syncstockapi

这样是会有问题的,应该换为(斜杠的问题)

gomobile bind -target=android gitlab/wallet/syncstockapi

另外如果你的项目下有2个包,也可以打成一个包:

//后面的路径用空格隔开即可

D:\\goWorks\\src>gomobile bind -target=android gitlab/wallet/syncstockapi gitlab/wallet/walletapi

这样打出来的包里面就会有2个api

温馨提示2

注意:所有下载的包都会默认放在GOPATH,因此这个目录是必须的,且一般包含src pkg bin三个目录,分别存放源码包,编译包以及可执行文件

执行go build会报一个错误:

解决方法如下:

将mobile更换为2018年的mobile文件夹,新版本的mobile会出现这个问题

温馨提示3,

出现上面的问题,那么就需要在C盘中进行go build了,需要在C盘中创建go\\src\\golang.org\\x\\mobile文件夹然后拷贝mobile进来
如图。

温馨提示4,

这个报错是说当前的go支持的是15以上的,但是NDK却是最低只支持16以上的,所以解决方案就是把C:\\Users\\33\\go\\src\\golang.org\\x\\mobile\\cmd\\gomobile下的env.go里面进行修改到支持16以上即可。参考:
https://github.com/golang/go/issues/27265

var ndk = ndkConfig
	"arm": 
		arch:       "arm",
		abi:        "armeabi-v7a",
		platform:   "android-15",
		gcc:        "arm-linux-androideabi-4.9",
		toolPrefix: "arm-linux-androideabi",
	,
	"arm64": 
		arch:       "arm64",
		abi:        "arm64-v8a",
		platform:   "android-21",
		gcc:        "aarch64-linux-android-4.9",
		toolPrefix: "aarch64-linux-android",
	,

	"386": 
		arch:       "x86",
		abi:        "x86",
		platform:   "android-15",
		gcc:        "x86-4.9",
		toolPrefix: "i686-linux-android",
	,
	"amd64": 
		arch:       "x86_64",
		abi:        "x86_64",
		platform:   "android-21",
		gcc:        "x86_64-4.9",
		toolPrefix: "x86_64-linux-android",
	,

修改为:

var ndk = ndkConfig
	"arm": 
		arch:       "arm",
		abi:        "armeabi-v7a",
		platform:   "android-16",
		gcc:        "arm-linux-androideabi-4.9",
		toolPrefix: "arm-linux-androideabi",
	,
	"arm64": 
		arch:       "arm64",
		abi:        "arm64-v8a",
		platform:   "android-21",
		gcc:        "aarch64-linux-android-4.9",
		toolPrefix: "aarch64-linux-android",
	,

	"386": 
		arch:       "x86",
		abi:        "x86",
		platform:   "android-16",
		gcc:        "x86-4.9",
		toolPrefix: "i686-linux-android",
	,
	"amd64": 
		arch:       "x86_64",
		abi:        "x86_64",
		platform:   "android-21",
		gcc:        "x86_64-4.9",
		toolPrefix: "x86_64-linux-android",
	,

以上是关于Android调用go语言 - 详细版的主要内容,如果未能解决你的问题,请参考以下文章

go 语言最详细的入门教程

go 语言最详细的入门教程

go语言语法(基础语法篇)

Golang Go语言中的 defer 怎么使用?

Go命令教程14. go env

内联函数和编译器对Go代码的优化