说实话,我一开始也以为用Go控制手机是个特别高大上的事儿,得用Java或者Kotlin写一堆底层代码,后来发现——不,Go就能干,而且干得还挺利索。
为什么是Go?不是Python不是Java?
你可能要问了:Python不是也能连手机吗?Java更是安卓的老本行,Go掺和什么?
其实我最早也是用Python写脚本连手机的,uiautomator2那套确实好用,但后来遇到一个问题——部署环境,客户那边是ARM的服务器,Python装依赖包装到想砸电脑,各种C扩展编译不过去,Go编译完一个二进制文件丢上去就跑,干净利落。
另外还有一个点,Go的并发模型特别适合处理手机连接这种场景,你要同时监控多台手机的状态,接收日志,发指令——Go的goroutine简直就像为这个量身定做的。
怎么连?三个主流方案
我折腾了大半个月,把市面上主流的Go连接手机方案都试了一遍,下面这张表你可以收藏,选方案的时候对照着看:
| 方案 | 原理 | 适用场景 | 学习成本 |
|---|---|---|---|
| ADB + exec.Command | 调用系统adb命令 | 快速原型、自动化测试 | 低 |
| gadb | 纯Go实现的ADB协议 | 生产环境、跨平台部署 | 中 |
| scrcpy-go | 屏幕镜像+控制 | 远程协助、投屏 | 中高 |
ADB命令包装(最省事)
这个方案其实没什么高深的,就是Go调用系统的adb命令。
package main
import (
"fmt"
"os/exec"
"strings"
)
func main() {
// 获取连接的设备列表
cmd := exec.Command("adb", "devices")
out, _ := cmd.Output()
lines := strings.Split(string(out), "\n")
for _, line := range lines {
if strings.Contains(line, "\tdevice") {
deviceID := strings.Split(line, "\t")[0]
fmt.Printf("发现设备:%s\n", deviceID)
}
}
}
你看,就这么几行代码,手机就连上了,我当年第一次跑通这个的时候,站在工位上喊了一句“卧槽,成了”,结果被隔壁组的同事白了一眼。
但这个方案有个隐患——你得保证运行环境里有adb,Docker环境下经常没有,这时候就得用方案二了。
gadb库(纯Go实现)
gadb这个库挺有意思,它把adb协议用Go重写了一遍,什么意思呢?就是你不需要系统装adb,Go代码自己就能和手机通信。
package main
import (
"fmt"
"log"
"github.com/electricbubble/gadb"
)
func main() {
client := gadb.NewClient()
devices, _ := client.ListDevices()
for _, device := range devices {
fmt.Printf("设备型号:%s\n", device.Model())
// 获取设备信息
properties, _ := device.GetProperties()
for key, value := range properties {
if key == "ro.build.version.sdk" {
fmt.Printf("安卓SDK版本:%s\n", value)
}
}
// 安装APK
err := device.Install("app-release.apk")
if err != nil {
log.Printf("安装失败:%v", err)
}
}
}
当时我拿着这个去给客户演示,客户问“你这个依赖什么环境”,我直接把二进制文件丢给他,说“双击运行就行”,那感觉,爽。
注意:用gadb之前,手机还是得开USB调试或无线调试,这个逃不掉。
scrcpy-go(屏幕控制)
如果你需要看到手机屏幕甚至远程操作,那就得上scrcpy了,Go这边有移植版本,虽然功能没有原版scrcpy那么全,但基本够用。
这个方案稍微复杂一点,涉及到视频流解码,我这里就不贴完整代码了——不然这篇文章得变成技术手册,核心就是:启动服务端,接收H264流,再推送到界面。
连不上?这五个坑我全踩过
说真的,我一开始连手机踩的坑比我写的代码还多。
驱动问题(Windows用户注意)
Windows上不装驱动连个鬼啊,我当时插上手机,adb devices出来一个空的列表,还以为代码写错了,查了半天发现是驱动没装,解决办法:去手机厂商官网下载USB驱动,或者用“驱动人生”这种工具自动装。
无线调试连不上
安卓11开始有无线调试功能,但必须是同一局域网,而且第一次连接还得用USB线配个对:
adb pair 192.168.1.100:41387 # 输入配对码 adb connect 192.168.1.100:16378
配对完才能无线连,我第一次不知道还要pair这一步,在路由器前坐了俩小时。
Go版本要求
gadb库要求Go 1.18以上,因为用到了泛型,你要是还在用老的1.16,赶紧升级吧,不然后面很多新库都用不了。
权限弹窗处理
当你用代码操作手机时,会频繁弹出“是否允许USB调试”“是否允许安装未知来源应用”这种弹窗,必须在手机上提前勾选“一律允许”,不然流程跑一半就卡住了。
多设备冲突

同时接两台手机,adb会有冲突,解决办法是在命令里指定设备:
cmd := exec.Command("adb", "-s", deviceID, "shell", "input", "tap", "500", "1000")
一个完整的例子:批量截图工具
说了这么多,来点实际的,我们写个工具,给连接的所有手机截图,并保存到本地。
package main
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"time"
)
func screenshotDevice(deviceID, saveDir string) {
// 截图
cmd := exec.Command("adb", "-s", deviceID, "shell", "screencap", "-p", "/sdcard/screen.png")
cmd.Run()
// 拉到本地
localPath := filepath.Join(saveDir, fmt.Sprintf("%s_%d.png", deviceID, time.Now().Unix()))
pullCmd := exec.Command("adb", "-s", deviceID, "pull", "/sdcard/screen.png", localPath)
pullCmd.Run()
fmt.Printf("✅ 设备 %s 截图已保存:%s\n", deviceID, localPath)
}
func main() {
// 取所有设备
out, _ := exec.Command("adb", "devices").Output()
devices := parseDevices(string(out))
if len(devices) == 0 {
fmt.Println("⚠️ 没发现任何设备,请检查连接")
return
}
saveDir := "screenshots"
os.MkdirAll(saveDir, 0755)
for _, device := range devices {
screenshotDevice(device, saveDir)
}
}
这个代码我跑了好多次,效果还不错。唯一的问题是,手机会有截图音效,如果同时连了10台手机,办公室里就会此起彼伏地响,你想象一下那个画面。
说点大实话
用Go连接手机,技术本身不难,难的是处理好各种边界情况——连不上怎么办?权限拒绝怎么办?adb服务挂了怎么办?
我建议你从方案一开始,先跑通一条最简单的命令,看到手机给你响应,那种“我的代码指挥了另一个设备”的爽感,会支撑你继续搞下去的。
而且说实话,用Go写这种工具类东西,真的会上瘾,编译快、部署简单、系统兼容性好,我现在连自己电脑上的一些小脚本都想用Go重写一遍。
至于复杂的GUI操作,还是交给Appium或者Airtest吧——Go没那么全能,但做一个连接手机的中间层,绰绰有余。
本文来自作者[kyadmin]投稿,不代表思利达立场,如若转载,请注明出处:http://kj.c-lida.com/post/18.html
评论列表(4条)
我是思利达的签约作者“kyadmin”!
希望本篇文章《用Go语言连接手机,这事儿比你想的简单》能对你有所帮助!
本站[思利达]内容主要涵盖:郑州思利达智能科技有限公司
本文概览:说实话,我一开始也以为用Go控制手机是个特别高大上的事儿,得用Java或者Kotlin写一堆底层代码,后来发现——不,Go就能干,而且干...