docs: 更新包文档,匹配 ADB 隧道架构和当前 API
This commit is contained in:
@@ -233,6 +233,20 @@ func PushFile(addr, serial, localPath, remotePath string, mode int, debug bool)
|
||||
return s.SyncPushFile(localPath, remotePath, mode, debug)
|
||||
}
|
||||
|
||||
// PushData 通过 ADB 推送内存数据到设备
|
||||
// 与 PushFile 功能相同,但数据来源是 []byte 而非本地文件
|
||||
// 适用于通过 go:embed 嵌入的资源文件
|
||||
func PushData(addr, serial string, data []byte, remotePath string, mode int, debug bool) (int64, error) {
|
||||
conn, err := ConnectToDevice(addr, serial, 15*time.Second)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
s := InitSync(conn)
|
||||
return s.SyncPushData(data, remotePath, mode, debug)
|
||||
}
|
||||
|
||||
// ---------- ADB 隧道(与 Python uiautomator2 完全一致) ----------
|
||||
|
||||
// CreateTunnel 建立到设备指定端口的 ADB 隧道
|
||||
|
||||
26
adb/doc.go
26
adb/doc.go
@@ -1,21 +1,27 @@
|
||||
// Package adb 实现了 Android Debug Bridge (ADB) 协议的核心功能。
|
||||
//
|
||||
// 本包提供了纯 Go 实现的 ADB 客户端,支持以下功能:
|
||||
// 本包提供了纯 Go 实现的 ADB 客户端,零外部依赖,支持以下功能:
|
||||
// - 设备发现与连接管理
|
||||
// - Shell 命令执行
|
||||
// - 文件同步传输(推送文件到设备)
|
||||
// - ADB 隧道(transport + tcp:<port>,与 Python adbutils 一致)
|
||||
// - 文件同步传输(Sync Push 协议)
|
||||
// - APK 安装
|
||||
// - UIAutomator2 服务启动
|
||||
//
|
||||
// ADB 隧道是本包的核心功能,用于替代 adb forward 端口转发。
|
||||
// 每次调用 CreateTunnel 会建立一条直通设备端口的原始 TCP 管道,
|
||||
// 无需占用本地端口,无需额外清理。
|
||||
//
|
||||
// 使用示例:
|
||||
//
|
||||
// // 连接 ADB 服务器
|
||||
// conn, err := adb.DialADB("127.0.0.1:5037", 15*time.Second)
|
||||
// // 列出所有设备
|
||||
// payload, _ := adb.ListDevicesRaw("127.0.0.1:5037", 15*time.Second)
|
||||
// devices := adb.ParseDevicesPayload(payload)
|
||||
//
|
||||
// // 建立 ADB 隧道到设备端口 9008
|
||||
// conn, err := adb.CreateTunnel("127.0.0.1:5037", "emulator-5554", 9008)
|
||||
// defer conn.Close()
|
||||
// // conn 现在是一条直通设备 9008 端口的 TCP 管道
|
||||
//
|
||||
// // 路由到指定设备
|
||||
// adb.TransportTo(conn, "emulator-5556")
|
||||
//
|
||||
// // 执行 Shell 命令
|
||||
// output, err := adb.ExecShell(conn, "getprop ro.product.model")
|
||||
// // 推送文件到设备
|
||||
// adb.PushFile("127.0.0.1:5037", "emulator-5554", "local.txt", "/sdcard/remote.txt", 0644, false)
|
||||
package adb
|
||||
|
||||
79
adb/sync.go
79
adb/sync.go
@@ -1,6 +1,7 @@
|
||||
package adb
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -119,6 +120,84 @@ func (s *Sync) SyncPushFile(localPath, remotePath string, mode int, debug bool)
|
||||
return total, nil
|
||||
}
|
||||
|
||||
// SyncPushData 将内存中的数据推送到设备
|
||||
// 与 SyncPushFile 功能相同,但数据来源是 []byte 而非本地文件
|
||||
// 适用于通过 go:embed 嵌入的资源文件
|
||||
func (s *Sync) SyncPushData(data []byte, remotePath string, mode int, debug bool) (int64, error) {
|
||||
// 初始化同步模式
|
||||
if err := s.StartSync(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// 构造 SEND 请求
|
||||
modeStr := strconv.Itoa(syscall.S_IFREG | mode)
|
||||
sendPayload := []byte(remotePath + "," + modeStr)
|
||||
|
||||
hdr := make([]byte, 8)
|
||||
copy(hdr[:4], []byte("SEND"))
|
||||
binary.LittleEndian.PutUint32(hdr[4:], uint32(len(sendPayload)))
|
||||
if _, err := s.Conn.Write(hdr); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if _, err := s.Conn.Write(sendPayload); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if debug {
|
||||
fmt.Printf("[调试] 发送 SEND 请求: 长度=%d 路径=%s 权限=%s 数据大小=%d\n", len(sendPayload), remotePath, modeStr, len(data))
|
||||
}
|
||||
|
||||
// 分块写入数据
|
||||
reader := bytes.NewReader(data)
|
||||
var total int64
|
||||
buf := make([]byte, maxChunk)
|
||||
for {
|
||||
n, rerr := reader.Read(buf)
|
||||
if n > 0 {
|
||||
dataHdr := make([]byte, 8)
|
||||
copy(dataHdr[:4], []byte("DATA"))
|
||||
binary.LittleEndian.PutUint32(dataHdr[4:], uint32(n))
|
||||
if _, err := s.Conn.Write(dataHdr); err != nil {
|
||||
return total, err
|
||||
}
|
||||
if _, err := s.Conn.Write(buf[:n]); err != nil {
|
||||
return total, err
|
||||
}
|
||||
total += int64(n)
|
||||
}
|
||||
if rerr != nil {
|
||||
if rerr == io.EOF {
|
||||
break
|
||||
}
|
||||
return total, rerr
|
||||
}
|
||||
}
|
||||
|
||||
// 发送 DONE 命令(使用当前时间戳)
|
||||
done := make([]byte, 8)
|
||||
copy(done[:4], []byte("DONE"))
|
||||
mtime := uint32(time.Now().Unix())
|
||||
binary.LittleEndian.PutUint32(done[4:], mtime)
|
||||
if _, err := s.Conn.Write(done); err != nil {
|
||||
return total, err
|
||||
}
|
||||
if debug {
|
||||
fmt.Println("[调试] 发送 DONE,等待响应")
|
||||
}
|
||||
|
||||
// 读取最终响应
|
||||
resp, msg, err := ReadSyncStatus(s.Conn)
|
||||
if err != nil {
|
||||
return total, err
|
||||
}
|
||||
if resp != "OKAY" {
|
||||
if len(msg) > 0 {
|
||||
return total, fmt.Errorf("同步失败: %s", string(msg))
|
||||
}
|
||||
return total, fmt.Errorf("同步失败: %s", resp)
|
||||
}
|
||||
return total, nil
|
||||
}
|
||||
|
||||
// StartSync 启动 ADB 同步模式
|
||||
// 发送 "sync:" 命令并等待 "OKAY" 响应
|
||||
func (s *Sync) StartSync() error {
|
||||
|
||||
Reference in New Issue
Block a user