Android 上的网络编程:使用 Go 语言

3
我一直在尝试在Android 10上运行Go应用程序,但遇到了问题。我相当确定现在知道为什么会遇到这个问题,但不确定如何解决它。首先,交叉编译可以使用简单的东西。例如,
package main
import "fmt"
func main() {
    fmt.Println("Hello, Android!")
}

可以通过以下命令编译成功:`GOARM=arm64 GOOS=linux go build`。但是如果现在添加一些网络代码:
package main

import (
    "fmt"
    "net/http"
    "io/ioutil"
)

func main() {
    fmt.Println("Hello, Android!")
    client := &http.Client{}
    resp, err := client.Get("https://www.google.com")
    if err != nil {
        fmt.Println(err)
        return
    }
    bodyBytes, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Printf("%s", bodyBytes)
}

并且编译的方式和运行的方式相同,但我会得到一个错误,例如:
Get "https://www.google.com": dial tcp: lookup www.google.com on [::1]:53: read udp [::1]:47618->[::1]:53: read: connection refused

起初我以为这是我的安卓设备上的一些网络配置问题,但经过了很多折腾后,我相信这与安卓的网络设置“不同”有关。也许这是一个Bionic vs libc的问题(?),但至少还有一个人似乎遇到了同样的问题,并通过使用NDK附带的工具链来解决。 但有没有人知道如何真正让它工作呢? 最初我希望它像GOOS=android GOARCH=arm64 go build那样容易,但失败了:
# command-line-arguments
/usr/lib/golang/pkg/tool/linux_amd64/link: running gcc failed: exit status 1
/usr/bin/ld: /tmp/go-link-384615804/go.o: Relocations in generic ELF (EM: 183)
<bunch of these>
/usr/bin/ld: /tmp/go-link-384615804/go.o: error adding symbols: file in wrong format
collect2: error: ld returned 1 exit status

一些谷歌搜索结果显示 https://github.com/golang/go/issues/30216 - 其中提到您需要显式启用CGO。但是GOARCH=arm64 GOOS=android CGO_ENABLED=1 go build仍然失败,这次的错误信息是:
# runtime/cgo
gcc_android.c:6:10: fatal error: android/log.h: No such file or directory
    6 | #include <android/log.h>
      |          ^~~~~~~~~~~~~~~
compilation terminated.

我尝试了很多其他组合,最接近的是将Go指向我的NDK安装中的编译器。基本上是像这样:GOARCH=arm64 GOOS=android CC=$NDKROOT/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-gcc CGO_ENABLED=1 CGO_CFLAGS="--sysroot $NDKROOT/sysroot -I$NDKROOT/sysroot/usr/include/arm-linux-androideabi/" go build。但是这失败了:
# runtime/cgo
In file included from _cgo_export.c:4:0:
cgo-gcc-export-header-prolog:25:14: error: size of array '_check_for_64_bit_pointer_matching_GoInt' is negative

我指向编译器的方式似乎有问题 - 例如,如果不指定sysroot,我将找不到任何头文件(stdlib.h),即使指定了它,也无法找到架构特定的包含文件(asm/types.h)而不指定其他包含路径。因此,错误可能是架构和编译器之间的不匹配,但我不确定在哪里或如何修复它。
如果您已经阅读了所有内容,感谢!一些额外的信息:
- Go版本:1.14.13 linux/amd64 - NDK版本:r16b
我也看了一些类似gomobile这样的东西,但那似乎更适用于构建Android应用程序(如apk),尽管我可能在这里错过了什么。
1个回答

2
我明白了!我的特定示例已经起作用,但我不太确定为什么会这样。答案基本上来自于https://github.com/golang/go/issues/20755,你需要做以下几步:
  • 构建NDK的独立版本(在执行此操作的Python脚本中,该注释表示它以更方便其他工具使用的方式打包)
  • 使用NDK中的交叉编译器重新构建Go标准库
  • 使用NDK交叉编译器构建应用程序。

因此,对于我的示例:

$ $(NDKROOT)/build/tools/make_standalone_toolchain.py --arch arm64 --api 24 --install-dir $(HOME)/ndk_standalone
$ CC=$(HOME)/ndk_standalone/bin/clang CXX=$(HOME)/ndk_standalone/bin/clang GOOS=android GOARCH=arm64 go install std
$ CC=$(HOME)/ndk_standalone/bin/clang CXX=$(HOME)/ndk_standalone/bin/clang GOOS=android GOARCH=arm64 CGO_ENABLED=1 go build

网络连接成功了!

我不确定为什么(或者是否)使用clang与直接调用编译器有所不同。也许我会再多尝试一下。此外,在编译我的应用程序时,我仍然需要显式启用CGO(但不适用于go std库),否则链接将失败。


干得好,解决了在编译https://github.com/inconshreveable/ngrok的安卓版本时遇到的同样问题,谢谢。 - undefined
不需要自己构建NDK独立环境,可以使用NDK自带的预构建环境。但我认为这仅适用于最新的构建工具(我的是29.x版本)。只需将CC和CXX设置为$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android24-clang即可。 - undefined
我的版本实际上是22.x,所以我猜你可以在所有稳定版本的ndk中获得预构建文件。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接