简介
有的时候要对手机UI界面进行滑动测试, 手动或许太消耗时间, 理由Android USB调试模式对UI进行上下滑动测试。
adb指令
使用adb --help 可以查看所有的adb支持指令, 但这里我们只需要上下, 使用到的指令:
adb devices #列举所有设备
adb -s 序列号 shell wm size # 获取指定设备的屏幕分辨率
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/c650c29d6be44e9d95fae456ee4ffd29.pngadb -s 序列号 shell input swipe startX startY endX endY
通过startX,startY 起点 到 endX,endY 终点, 实现左/右/上/下滑动
代码
action.go
package main
import (
"encoding/json"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"sync"
"time"
)
type Direction int
const (
Move_LEFT Direction = iota
Move_RIGHT = 1
Move_UP = 2
Move_DOWN = 3
)
type IJsonUnmarshal interface {
FromJsonBytes(src []byte) error
}
type ActionConfig struct {
BaseConfig CommandConfig `json:"base_config,omitempty"`
ActionItems []ActionItem `json:"action_items,omitempty"`
}
func (a *ActionConfig) FromJsonBytes(src []byte) (err error) {
err = json.Unmarshal(src, a)
return
}
type ActionItem struct {
Direction Direction `json:"direction"`
BeforeSleep int64 `json:"before_sleep,omitempty"`
AfterSleep int64 `json:"after_sleep,omitempty"`
StartX int `json:"start_x"`
StartY int `json:"start_y"`
EndX int `json:"end_x"`
EndY int `json:"end_y"`
}
type DeviceInfoItem struct {
Width int
Height int
SerialNumber string
}
func getActionConfig() (actCfgs ActionConfig, err error) {
var f *os.File
f, err = os.Open("./cfg.json")
if nil != err {
return
}
defer f.Close()
var src []byte
src, err = io.ReadAll(f)
if nil != err {
return
}
err = actCfgs.FromJsonBytes(src)
if nil != err {
return
}
if actCfgs.BaseConfig.WorkPath == "." {
var exePath string
exePath, err = os.Executable()
fmt.Println("1111: ", exePath)
if err != nil {
return
}
actCfgs.BaseConfig.WorkPath = filepath.Dir(exePath)
}
return
//actCfgs = ActionConfig{
// ActionItems: []ActionItem{
// {Move_UP, 0, 5 * time.Second, 0, 0, 0, 0},
// {Move_UP, 0, 5 * time.Second, 0, 0, 0, 0},
// {Move_UP, 0, 5 * time.Second, 0, 0, 0, 0},
// {Move_UP, 0, 5 * time.Second, 0, 0, 0, 0},
// {Move_UP, 0, 5 * time.Second, 0, 0, 0, 0},
// {Move_UP, 0, 5 * time.Second, 0, 0, 0, 0},
// {Move_DOWN, 0, 5 * time.Second, 0, 0, 0, 0},
// {Move_DOWN, 0, 5 * time.Second, 0, 0, 0, 0},
// {Move_DOWN, 0, 5 * time.Second, 0, 0, 0, 0},
// {Move_DOWN, 0, 5 * time.Second, 0, 0, 0, 0},
// {Move_DOWN, 0, 5 * time.Second, 0, 0, 0, 0},
// {Move_DOWN, 0, 5 * time.Second, 0, 0, 0, 0},
// },
//}
//actCfgs.BaseConfig, err = GetBaseCommandConfig()
}
func doCommandAction(cmdCfg CommandConfig, callback func(output []byte) error) error {
fmt.Println(cmdCfg)
// 创建Cmd结构体
cmd := exec.Command(cmdCfg.ExePath, cmdCfg.Arguments...)
cmd.Dir = cmdCfg.WorkPath
// 运行命令并获取输出
output, err := cmd.CombinedOutput()
if err != nil {
fmt.Println("1-Error:", err)
return err
}
err = callback(output)
return err
}
func getSerialNumberAction(cmdCfg CommandConfig) (serials []string, err error) {
cmdCfg.Arguments = []string{"devices"}
err = doCommandAction(cmdCfg, func(output []byte) error {
lines := strings.Split(string(output), "\n")
serials = make([]string, 0)
for i, line := range lines {
if 0 == i {
continue
}
fields := strings.Fields(line)
if len(fields) > 0 {
serials = append(serials, fields[0])
//fmt.Println(fields[0])
}
}
return nil
})
return
}
func getDeviceInfo(cmdCfg CommandConfig, serialNumber string) (item DeviceInfoItem, err error) {
item.SerialNumber = serialNumber
cmdCfg.Arguments = []string{"-s", serialNumber, "shell", "wm", "size"}
err = doCommandAction(cmdCfg, func(output []byte) error {
fields := strings.Fields(string(output))
if 3 > len(fields) {
return fmt.Errorf("fail to get device info")
}
strs := strings.Split(fields[2], "x")
var temp int64
temp, _ = strconv.ParseInt(strs[0], 10, 64)
item.Width = int(temp)
temp, _ = strconv.ParseInt(strs[1], 10, 64)
item.Height = int(temp)
return nil
})
return
}
func getActionItem(item DeviceInfoItem, actionItem *ActionItem) error {
actionItem.StartX = item.Width / 2
actionItem.EndX = item.Width / 2
actionItem.StartY = item.Height / 2
switch actionItem.Direction {
case Move_UP:
actionItem.EndY = actionItem.StartY / 2
case Move_DOWN:
actionItem.EndY = actionItem.StartY + actionItem.StartY/2
case Move_LEFT:
case Move_RIGHT:
default:
break
}
return nil
}
func deviceAction(waiter *sync.WaitGroup, actCfgs ActionConfig, serialNumber string) error {
defer waiter.Done()
item, err := getDeviceInfo(actCfgs.BaseConfig, serialNumber)
if nil != err {
return err
}
for i := range actCfgs.ActionItems {
err = getActionItem(item, &actCfgs.ActionItems[i])
if nil != err {
return err
}
}
cfg := actCfgs.BaseConfig
cfg.Arguments = []string{"-s", serialNumber, "shell", "input", "swipe", "startX", "startY", "endX", "endY"}
for {
for i := range actCfgs.ActionItems {
fmt.Println(actCfgs.ActionItems[i])
time.Sleep(time.Duration(actCfgs.ActionItems[i].BeforeSleep) * time.Millisecond)
cfg.Arguments[len(cfg.Arguments)-4] = strconv.Itoa(actCfgs.ActionItems[i].StartX)
cfg.Arguments[len(cfg.Arguments)-3] = strconv.Itoa(actCfgs.ActionItems[i].StartY)
cfg.Arguments[len(cfg.Arguments)-2] = strconv.Itoa(actCfgs.ActionItems[i].EndX)
cfg.Arguments[len(cfg.Arguments)-1] = strconv.Itoa(actCfgs.ActionItems[i].EndY)
if err = doCommandAction(cfg, func(output []byte) error {
return nil
}); nil != err {
return err
}
time.Sleep(time.Duration(actCfgs.ActionItems[i].AfterSleep) * time.Millisecond)
}
}
}
func StartAction() error {
var actCfgs, err = getActionConfig()
var waiter sync.WaitGroup
var serials []string
serials, err = getSerialNumberAction(actCfgs.BaseConfig)
if nil != err {
return err
}
if 0 >= len(serials) {
fmt.Println("not devices!!!")
return nil
}
for i := range serials {
waiter.Add(1)
go deviceAction(&waiter, actCfgs, serials[i])
}
waiter.Wait()
return nil
}
config.go
package main
import (
"fmt"
"os"
"path/filepath"
)
const (
adbDefaultPath = "D:\\Softwares\\Paths\\Android\\android-sdk\\platform-tools"
)
type CommandConfig struct {
WorkPath string `json:"work_path"`
ExePath string `json:"exe_path"`
Arguments []string `json:"arguments,omitempty"`
}
func GetBaseCommandConfig() (cfg CommandConfig, err error) {
var (
exePath string
)
exePath, err = os.Executable()
fmt.Println("1111: ", exePath)
if err != nil {
return
}
cfg.WorkPath = filepath.Dir(exePath)
cfg.ExePath = fmt.Sprintf("%s\\%s", adbDefaultPath, "adb.exe")
fmt.Println("11112: ", cfg)
return
}
main.go
package main
import (
"fmt"
)
func main() {
err := StartAction()
if nil != err {
fmt.Println(err)
return
}
}
配置文件, 放到程序同级目录
cfg.json
{
"base_config": {
"work_path": ".",
"exe_path": "D:/Softwares/Paths/Android/android-sdk/platform-tools/adb.exe"
},
"action_items": [
{
"direction": 2,
"before_sleep": 0,
"after_sleep": 2000
}
]
}
最后
将手机调试模式打开, 连上电脑,执行程序,需要什么动作在配置文件:
- 支持多台设备;
- 支持多个动作, 都可以在配置文件中添加;
- 支持动作前后延时;
其他
ADB指令基本用法&shell指令
ADB指令基本用法(官网)