zoukankan      html  css  js  c++  java
  • 重新梳理一下adb操作app(golang版)

    主要参考我之前整理的内容https://www.cnblogs.com/pu369/p/10490668.html,梳理简化一下思路,以便于用最简单的代码来应对无聊人士的要求。

    需求主要是:打开手机、切换到主页面、找页面关键字、点击、滑动、返回、杀死APP;当然,延时是必须的。

    另外我的手机特点是:取消了密码开机,用电源键开机后有广告、但点按home键后会显示主页面

    步骤:

    1、新建main.go  (有些引用的包可能不需要,具体的一些函数定义在后面)如下:

    package main
    
    import (
        "crypto/md5"
        "fmt"
        "image/png"
        "io/ioutil"
    
        "log"
        "os"
        "regexp"
        "strings"
    
        "bytes"
        "os/exec"
        "strconv"
        "time"
    
        "github.com/robfig/cron"
    )
    
    
    func main() {
        dowork()
        c := cron.New()
        c.AddFunc("0 0 12 * * ?", dowork)
        c.Start()
        select {}
    
    }
    
    func dowork(){
    ...
    }
    
    ...

    说明:main函数主要是用select{}让程序永远执行下去;同时,引用github.com/robfig/cron (可参考:https://www.cnblogs.com/liuzhongchao/p/9521897.html)  使程序每天12点定时执行。(当然,也可参考这里自己实现定时功能:https://www.jianshu.com/p/4955e5d652ae

    2、dowork()函数仅用于调用后面的函数

    func dowork() {
    //点击
    AdbShellInputTap()
    //延时
    TimeSleepDuration()
    ... 
    }

    3、延时函数(等待页面加载、或APP要求用户查看多长时间,时间单位为秒)

    func TimeSleepDuration(x int) {
        time.Sleep(time.Duration(x) * time.Second)
    }

    4、打开(关闭)手机电源(为简化就不判断手机是否休眠了,其实是模拟按键,我会找些键值附在本文最后面)

    AdbShellInputKeyEvent("26") //power
    func AdbShellInputKeyEvent(s string) {
        exec.Command("adb", "shell", "input", "keyevent", s).Run()
    }

    5、切换到手机主页面

    由我的手机没密码,打开电源后有广告,按home会回到主页面,

    就用步骤4的AdbShellInputKeyEvent函数模拟home键,在代码中可以多按两次。

    AdbShellInputKeyEvent("3") //home

    常的还有:  4表示back返回键

    6、打开某个APP-直接用下面的AdbShellInputTap(X坐标、Y坐标)函数模拟点击

    打开手机  设置-通用-开发者选项-指针位置。将APP放在手机主页面,然后点按APP图标的中心位置查看坐标 X,Y

    func AdbShellInputTap(x, y int) {
        x2 := strconv.Itoa(x)
        y2 := strconv.Itoa(y)
        exec.Command("adb", "shell", "input", "tap", x2, y2).Run()
    }

    7、想点击页面包含某个“关键字”的区域

    7.1  首先要将手机屏幕显示页面的源码截取到电脑上,用AdbShellUiautomatorDump()得到window_dump.xml并复制到了当前go程序所在的目录(由于从手机复制到电脑需要时间,所以在这个代码中加了延时2秒,以等待复制完成。实际使用中有可能要根据你的设备速度调整这里的延时时间)

    func AdbShellUiautomatorDump() {
        //删除当前目录下的window_dump.xml
        exec.Command("cmd", "/c", "del", "-y", "window_dump.xml").Run()
        //重新dump
        exec.Command("adb", "shell", "uiautomator", "dump", "/sdcard/window_dump.xml").Run()
        exec.Command("adb", "pull", "/sdcard/window_dump.xml", ".").Run()
        exec.Command("adb", "shell", "rm", "/sdcard/window_dump.xml").Run()
      TimeSleepDuration(2)
    }

    7.2  点击“关键字”,需要指明点击找到的第几个(用正则表达式匹配),用0表示第一个。通常是Tap("关键字",0)

    func Tap(s string, ix int) {
        //先执行AdbShellUiautomatorDump函数。
        AdbShellUiautomatorDump()
    file, _ := os.Open("window_dump.xml") defer file.Close() doc, _ := ioutil.ReadAll(file) doc1 := string(doc) ss := fmt.Sprintf("%s%s%s", `<node.[^>]+?`, s, `.[^>]+?[(d+),(d+)][(d+),(d+)].+?>`) r := regexp.MustCompile(ss) match := r.FindAllStringSubmatch(doc1, -1) le := len(match) //匹配到1个或多个,ixx表示匹配到的第几个 ixx := ix if le == 0 { log.Println("未匹配到:", s) return } if ix < 0 { ixx = le + ix } if ixx < 0 { ixx = 0 } if ixx >le { ixx = le } x1, _ := strconv.Atoi(fmt.Sprint(match[ixx][1])) y1, _ := strconv.Atoi(fmt.Sprint(match[ixx][2])) x2, _ := strconv.Atoi(fmt.Sprint(match[ixx][3])) y2, _ := strconv.Atoi(fmt.Sprint(match[ixx][4])) xx := (x2-x1)/2 + x1 yy := (y2-y1)/2 + y1 //log.Println(s) AdbShellInputTap(xx, yy) }

    8、更复杂的需求,如:

    主页面上有N个链接(每个链接的特点是:包括年-月-,类似2019-1-),点第1个,打开一篇文章,看几分钟后,返回;类似的,点第2个链接.......最后,点开第N个链接,查看几分钟后,返回主页面,再向下翻页4次。

    实现:开始以为要用递归,后来发现在golang中用for循环即可。为了层次清晰,分成以下几个函数

    8.1在步骤2的dowork函数中,写以下循环,表示主页面要向下翻4次(页)

        //学习文章
        for i := 0; i < 5; i++ {
            fmt.Println("article", i)
            getArticles()
        }

    8.2 getArticles中,首先findxxbydate(我用xx表示链接的意思:-)用正则查找当前页中,所有包含包括年-月-,即类似2019-1-的链接,存入二维切片[][]string中。

    然后用for循环打开每个链接指向的文章,并停留350秒,然后按返回键。当页面所有链接循环操作完毕后,将主页面向上滚动1000px(这个值根据手机页面的实际滚动区域高度来确定。getArticles执行完毕后,进入下一个主循环。

    getArticles代码:

    func getArticles() {
        //找当前UI中“年-月-”,写入[][]string
        xx := findxxbydate()
        for _, v := range xx {
            //循环打开
            TapRegion4xx(v, 350)
        }
        //向上滚动1000
        AdbShellInputSwipe(500, 1765, 500, 355)
        TimeSleepDuration(6)
    }

    8.3 上面的findxxbydate用于查找当前手机页面上所有匹配正则表达式的位置,存入二维切片(要根据需要修改ss正则表达式)

    func findxxbydate() [][]string {
        //先执行AdbShellUiautomatorDump函数。
        AdbShellUiautomatorDump()
        file, _ := os.Open("window_dump.xml")
        defer file.Close()
        doc, _ := ioutil.ReadAll(file)
        doc1 := string(doc)
        ss := `<node.[^>]+?d{4}-d{2}-.[^>]+?[(d+),(d+)][(d+),(d+)].[^>]+?>`
        r := regexp.MustCompile(ss)
        match := r.FindAllStringSubmatch(doc1, -1)
        return match
    }

    8.4   TapRegion4xx(v, 350)用在步骤8.2的for range循环中,用于点击“年-月-”数组中的每一个链接,并在点开后,停留350秒,后返回

    func TapRegion4xx(match []string, t int) {
        x1, _ := strconv.Atoi(fmt.Sprint(match[1]))
        y1, _ := strconv.Atoi(fmt.Sprint(match[2]))
        x2, _ := strconv.Atoi(fmt.Sprint(match[3]))
        y2, _ := strconv.Atoi(fmt.Sprint(match[4]))
    
        xx := (x2-x1)/2 + x1
        yy := (y2-y1)/2 + y1
        AdbShellInputTap(xx, yy)
        TimeSleepDuration(t)
        AdbShellInputKeyEvent("4") //back
        TimeSleepDuration(2)
    }

    8.6 模拟滑动手机页面 AdbShellInputSwipe

    //模拟滑动
    //adb shell input swipe  0 0  600 600
    func AdbShellInputSwipe(x1, y1, x2, y2 int) {
        xx1 := strconv.Itoa(x1)
        yy1 := strconv.Itoa(y1)
        xx2 := strconv.Itoa(x2)
        yy2 := strconv.Itoa(y2)
        exec.Command("adb", "shell", "input", "swipe", xx1, yy1, xx2, yy2).Run()
    }

    注意:之前曾误以为手机页面对应的源码像电脑上的html5一样包括不可见部分。后来才发现每次获取的手机页面源码只包含可见部分。也就是说只要滑动手机屏幕就必须重新用AdbShellUiautomatorDump获取。

    另外,关于判断设备是否休眠、查看手机上应用的packageName、查看最上层activity名字等功能,可参考我之前的文章https://www.cnblogs.com/pu369/p/10490668.html

  • 相关阅读:
    vs2012无法启动已配置的开发Web服务器
    VS2013无法启动 IIS Express Web解决办法
    Windows server 2008系统基本优化
    Asp.net mvc项目架构分享系列之架构搭建初步
    sql windows server2008 全套激活码
    (copy)MVC4.0网站发布和部署到IIS7.0上的方法
    (转) C#多线程赛跑实例
    (已解决) 未能加载文件或程序集“Newtonsoft.Json, Version=4.0.0.0, Culture=neutral,
    C# HttpWebRequest 绝技 转至 http://www.sufeinet.com/
    string 与char* char[]之间的转换 2015-04-09 11:30 29人阅读 评论(0) 收藏
  • 原文地址:https://www.cnblogs.com/pu369/p/12073899.html
Copyright © 2011-2022 走看看