Auto.js 自动化工作流 JavaScript IDE

Auto.js 自动化工作流 JavaScript IDE
image-20220503084359015

「官方文档」大家可以阅读使用文档,熟悉里面的一些方法,理解作者的设计思路和理念 Auto.js Pro:pro.autojs.org/docs/#/zh-c…Auto.js :hyb1996.github.io/AutoJs-Docs…

Auto.js是什么

一个在Android、鸿蒙平台编写、运行JavaScript代码的集成开发环境,包括代码补全的编辑器、单步调试、图形化设计,可构建为独立apk应用,也可连接电脑开发。

Auto.js能做什么

创建「自动化工具」「效率工具」、优美界面、小应用,诸如早晨自动签到、处理文件为excel、图片批量处理、「机器人」、自动化测试、搭建服务器等,或解放双手,或学习编程,或制作应用。

Auto.js版本

V4:免费版

V8:收费版。现行主要版本,javascript引擎 Rhino 1.7.7.2,

V9:「基于Node.js的引擎和全新的API」,伴随着庞大的npm生态(接近200万个npm包),并仍然支持和Android/Java交互(也即可在Node.js中使用Android/Java API)。

API

详细api文档:https://pro.autojs.org/docs/#/zh-cn/

  • app: 应用。启动应用,卸载应用,使用应用查看、编辑文件、访问网页,发送应用间广播等。

  • events: 事件与监听。按键监听,通知监听,触摸监听等。

  • images, colors: 图片和图色处理。截图,剪切图片,找图找色,读取保存图片等。

  • threads: 多线程支持。

  • console: 控制台。记录运行的日志、错误、信息等。

  • device: 设备。获取设备屏幕宽高、系统版本等信息,控制设备音量、亮度等。

  • engines: 脚本引擎。用于启动其他脚本。

  • floaty: 悬浮窗。用于显示自定义的悬浮窗。

  • files: 文件系统。文件创建、获取信息、读写。

  • http: HTTP。发送HTTP请求,例如GET, POST等。

  • keys: 按键模拟。比如音量键、Home键模拟等。

  • shell: Shell命令。

  • ui: UI界面。用于显示自定义的UI界面,和用户交互。

Auto.js使用VSCode调试

使用vscode开发autojs脚本请参考:https://blog.autojs.org/2021/07/05/vscode-debug/

温馨提示:vscode安装Auto.js-VSCodeExt插件(针对Autojs V4版本)Auto.js-Pro-Ext插件(针对Auto.js Pro版)

开发三步曲

第一步 打开软件

打开需要开发脚本的软件是我们的前提,以下几种方式可供我们去打开App,以下函数均可以作为全局函数使用。

app.launchApp(appName)

  • appName {string} 应用名称

通过应用名称启动应用。如果该名称对应的应用不存在,则返回false; 否则返回true。如果该名称对应多个应用,则只启动其中某一个。

launchApp("微信");

app.launch(packageName)

  • packageName {string} 应用包名

通过应用包名启动应用。如果该包名对应的应用不存在,则返回false;否则返回true。

// 启动微信
launch("com.tencent.mm");

app.getPackageName(appName)

  • appName {string} 应用名称

获取应用名称对应的已安装的应用的包名。如果该找不到该应用,返回null;如果该名称对应多个应用,则只返回其中某一个的包名。

const name = getPackageName("QQ"); // 返回"com.tencent.mobileqq"

app.getAppName(packageName)

  • packageName {string} 应用包名

获取应用包名对应的已安装的应用的名称。如果该找不到该应用,返回null。

const name = getAppName("com.tencent.mobileqq"); // 返回"QQ"

// 启动微信
launchApp("微信");
launch("com.tencent.mm");

// 返回"com.tencent.mobileqq"
getPackageName("QQ"); 

// 返回"QQ"
getAppName("com.tencent.mobileqq");

第二步 找控件

一般软件的界面是由一个个控件构成的,例如图片部分是一个图片控件(ImageView),文字部分是一个文字控件(TextView);同时,通过各种布局来决定各个控件的位置,例如,线性布局(LinearLayout)里面的控件都是按水平或垂直一次叠放的,列表布局(AbsListView)则是以列表的形式显示控件。为了确定我们需要的控件,我们需要对控件进行分析,分析有2种方式【需要开启autojs的悬浮窗】:

Auto.js 自动化工作流 JavaScript IDE
布局分析

分析控件

布局范围分析

Auto.js基于控件的操作可以帮助我们选择屏幕上的控件,获取其信息或对其进行操作。对于一般软件而言,对不同机型有很好的兼容性;但对于游戏而言,由于游戏界面并不是由控件构成,我们选择Auto.js基于坐标的操作。常见控件的类名如下:

  • android.widget.TextView 文本控件
  • android.widget.ImageView 图片控件
  • android.widget.Button 按钮控件
  • android.widget.EditText 输入框控件
  • android.widget.AbsListView 列表控件
  • android.widget.LinearLayout 线性布局
  • android.widget.FrameLayout 帧布局
  • android.widget.RelativeLayout 相对布局
  • android.widget.RelativeLayout 相对布局
  • android.support.v7.widget.RecyclerView 通常也是列表控件
Auto.js 自动化工作流 JavaScript IDE
1650183041244-e0b78997-50fb-4390-bb64-f69c3931dd02

控件的具体信息可参考:https://pro.autojs.org/docs/#/zh-cn/widgetsBasedAutomation?id=uiselector

布局层次分析
Auto.js 自动化工作流 JavaScript IDE
image-20220503092144440

显而易见,组件以树的结构组织,一个又一个堆叠在一起。有时候我们不管使用id,还是desc,或是text等属性进行选择都无法定位我们想要的组件或者进行一些操作,因此,我们需要利用组件之间的关系间接实现效果。

确定控件

基于控件属性确定控件

当分析了很多控件后不难发现,控件的文本信息往往是以desc或text属性来呈现的,比如一个该按钮名为”朋友圈”,其控件的desc属性或text属性就是其按钮名字,确定其名字也就确定了此控件。

Auto.js 自动化工作流 JavaScript IDE
image-20220503092850273

所以我们就可以通text()方法来选中这个控件:

// 选中控件
text('朋友圈')

// 判断控件是否存在
text("朋友圈").exists()

// 当"朋友圈"不存在时,续循环等待,直到按钮出现
while(!text("朋友圈").exists()) {
  sleep(1000);
}

// 对该控件执行点击操作
desc("朋友圈").findonce().click();

有些时候我们可能只知道包含的部分desc或text,这时候可用这四个函数来确定:

// 为当前选择器附加控件"desc需要包含字符串"的筛选条件。
if (descContains("选项").exists()){
    // do something ...
}

// 为当前选择器附加控件"desc需要以xxx开头"的筛选条件。
if (descStartsWith("更多").exists()){
   // do something ...
}

// 同理,text也有对应的两个函数
// 为当前选择器附加控件"text需要包含字符串"的筛选条件。
if (textContains("选项").exists()){
    // do something ...
}

// 为当前选择器附加控件"text需要以xxx开头"的筛选条件。
if (textStartsWith("更多").exists()){
   // do something ...
}

当然,有多情况是desctext属性并不能唯一确定要操作的控件,甚至有时候为空。这时我们往往用其他属性(常用的有className、id、depth等)一起叠加使用来尽可能的使之唯一,比如:

const myControl = className(“XXX”).depth(xx).id(“XXX”).text(“XXX”).findone();

注意:在不同的Android系统上,同一版本的app的控件属性可能会变化。同样,app版本更新后,控件属性、布局也可能发生变化,所以,若要对不同版本不同系统具有兼容性,应尽可能选择不变化的控件属性。

基于控件父子关系确定控件

有时候,控件属性是变化的、随机的,这时候我们便不能通过控件的本身固有属性如id,className,depth等来唯一的确定出需要的控件,这时候我们可以通过控件之间的上下级关系(也叫父子关系)来确定出需要操作的控件。父子关系嵌套两三层往往即可确定出另一控件。

Auto.js 自动化工作流 JavaScript IDE
image-20220503105453522

看到上面图片,有时候我们会发现当前控件无法点击,所以我们也要获取其父控件。如下:

const control = id('com.tencent.mm:id/hfx').findOne()
log(control.clickable()) // false
log(control.parent().clickable()) // false
log(control.parent().parent().clickable()) // false
log(control.parent().parent().parent().clickable()) // false
log(control.parent().parent().parent().parent().clickable()) // true

toast('success')

需要注意的是,确定控件时要加上“.findone()”,此方法调用后会返回所有符合条件的控件集合。因此,首先要找到一个不容易变化的或容易找的控件“.findone()”后再在此基础上进行“.parent()”“.child(index)”操作找到另一控件。

基于坐标来确定控件

如果一个控件本身无法通过click()点击,即clickable属性为false,除了寻找该控件的父级控件,还可以利用bounds()函数获取其坐标,再利用坐标点击。基于坐标来确定要操作的控件比较简单,核心是确定要操作的控件的坐标即可。Auto.JS里可以直接获取控件的坐标,每一个控件包含其“.bounds()”属性,bounds()其实表示的是一个范围矩阵。Auto.js 自动化工作流 JavaScript IDE在获得控件的坐标**bounds()**属性后,就可以对控件执行基于坐标的操作,常见的操作有:

bounds().left:长方形左边界的x坐标

bounds().right:长方形右边界的x坐标

bounds().top:长方形上边界的y坐标

bounds().bottom:长方形下边界的y坐标

bounds().centerX():长方形中点x坐标

bounds().centerY():长方形中点y坐标

bounds().width():长方形宽度也就是控件宽度

bounds().height():长方形高度也就是控件高度

click(x,y):坐标(x,y)处执行点击操作  //注意:安卓7以下点击需要root权限且函数为Tap(x,y)

第三步 操作

https://pro.autojs.org/docs/#/zh-cn/widgetsBasedAutomation?id=uiobject

查找函数find/findOne/findOnce

查找函数可以根据我们设定的一些属性对屏幕上的控件进行搜索和查找,并返回符合条件的控件。因此,想要对返回的控件进行下一步操作的前提是必须要执行一下查找函数。三个搜索函数“.find()” “.findOne()” “.findOnce()”在使用上是有点差别的,具体如下:

文档 https://pro.autojs.org/docs/#/zh-cn/widgetsBasedAutomation?id=uiselectorfindone

find()
// 根据当前的选择器所确定的筛选条件,对屏幕上的控件进行搜索,找到所有满足条件的控件集合并返回。这个搜索只进行一次,并不保证一定会找到,因而会出现返回的控件集合为空的情况。
const c = className("AbsListView").find();
// 可以通过empty()函数判断找到的是否为空
if(!c.empty()){
    toast("找到啦");
}else{
    toast("没找到╭(╯^╰)╮");
}
findOne()

根据当前的选择器所确定的筛选条件,对屏幕上的控件进行搜索,直到屏幕上出现满足条件的一个控件为止,并返回该控件。如果找不到控件,当屏幕内容发生变化时会重新寻找,直至找到。需要注意的是,「如果屏幕上一直没有出现所描述的控件,则该函数会阻塞,直至所描述的控件出现为止」。因此此函数不会返回null

const control1 = textContains("2022-05-01").findone(); // 找到一个包含"2022-05-01"的控件
const control2 = textContains("2022-05-01").findone(500); // 在500毫秒内找到一个包含"2022-05-01"的控件,若找不到,终止搜索返回null
findOnce()

根据当前的选择器所确定的筛选条件,对屏幕上的控件进行搜索,如果找到符合条件的控件则返回该控件;否则返回null

const control1 = textContains("2022-05-01").findonce(); 

// 根据当前的选择器所确定的筛选条件,对屏幕上的控件进行搜索,并返回第 i + 1 个符合条件的控件;如果没有找到符合条件的控件,或者符合条件的控件个数 < i, 则返回null。
const control2 = textContains("2022-05-01").findonce(1); // 搜索第二个包含"2022-05-01"的控件

click

「click」是点击操作,实际上,使用「click」函数我们可以对坐标进行点击,也可以对控件进行点击,最后也可以对某些特定字符点击,总结如下:

//一:根据控件属性唯一确定出控件后,再对控件进行点击click操作
desc("更多选项").depth(9).findone().click();
text("注册").click();


//二:根据坐标来实现基于坐标的点击
setScreenMetrics(10801920);        //声明是基于分辨率1080,1920的点击
click(800200);                     //分辨率1080,1920下点击(800,200)


//三:有时候控件可能是个Image或是不可点击的(clickable=false),这时我们可以对屏幕进行点击
click("2020-07-15");                  //点击"2020-07-15"
click("2020-07-15",0);                //点击第一个"2020-07-15"
click("str",index);                   //点击第index个字符"str"(因为有时str会出现多次,另外注意下标从0开始)

另外,因为不同设备的分辨率大小是不同的,那么我们**click(x,y)「在不同分辨率下就会出错,Auto.JS针对这一问题内置了一个函数」“setScreenMetrics(width, height)”**,具体用法如下:

// 设置在特定屏幕分辨率下要点击的坐标值(x,y)
setScreenMetrics(10801920); // 声明是基于分辨率1080,1920的点击
click(800200); // 分辨率1080,1920下点击(800,200)
longClick(300500); // 分辨率1080,1920下长点击(300,500)

当我们使用540,960分辨率的设备(x,y各缩小了一半)后执行上述代码时,他会自动计算缩放的比例,从而实际点击的是坐标(400,100)和(150,250)这两个坐标值。当然,基于坐标的操作不仅有click()操作,常用的还有swipe()滑动操作,gesture()手势滑动操作等,具体可查看官方文档。

上下滑动和翻页

对整个屏幕滑动

swipe(x1, y1, x2, y2, duration)

  • x1 {number} 滑动的起始坐标的x值
  • y1 {number} 滑动的起始坐标的y值
  • x2 {number} 滑动的结束坐标的x值
  • y2 {number} 滑动的结束坐标的y值
  • duration {number} 滑动时长,单位毫秒

模拟从坐标(x1, y1)滑动到坐标(x2, y2),并返回是否成功。只有滑动操作执行完成时脚本才会继续执行。

一般而言,只有滑动过程中被其他事件中断才会滑动失败。

const h = device.height // 屏幕高
const w = device.width // 屏幕宽
const x1 = (w / 3) * 2 // 横坐标3分之2处
const x2 = (w / 3) * 1 // 横坐标3分之1处
const h1 = (h / 6) * 3 // 纵坐标6分之3处
const h2 = (h / 6) * 5 // 纵坐标6分之5处
const h3 = h / 6

swipe(x1, h1, x2, h1, 50// 向左滑
swipe(x2, h1, x1, h1, 50// 向右滑
swipe(x1, h1, x1, h2, 50// 向下滑
swipe(x1, h2, x1, h3, 50// 向上滑

对控件进行滑动

scrollForward(): 对控件执行向前滑动的操作,并返回是否操作成功。向前滑动包括了向右和向下滑动。如果一个控件既可以向右滑动和向下滑动,那么执行scrollForward()的行为是未知的。

scrollBackward(): 对控件执行向后滑动的操作,并返回是否操作成功。向后滑动包括了向右和向下滑动。如果一个控件既可以向右滑动和向下滑动,那么执行scrollForward()的行为是未知的。

id("jr").findOne().scrollForward()
sleep(1000)
id("jr").findOne().scrollBackward()

案例

朋友圈点赞


toast('start!')

// 点赞数量
let sum = 0
// 是否在可视区域的底部安全距离
const BOTTOM_SAFE_DISTANCE = 200
// 滚动结束后的sleep
const SCROLL_SLEEP = 1000
// 打开点赞tag后延迟200进行点赞
const OPEN_LIKE_TAG_SLEEP = 200
// 点赞结束后的延迟时间
const EXECUTE_LIKE_OVER_SLEEP = 300

// 滚动
function scrollTo(pixel{
  swipe(
    device.width / 2,
    device.height / 2,
    device.width / 2,
    device.height / 2 + pixel,
    800
  )
  // sleep(SCROLL_SLEEP)
  sleep(1000)
}

// 判断是否在可视区域,不在可视区域则滚动到可视区域
function isNeedScroll(control{
  // 设备高度
  const h = device.height
  // 控件最底部的位置p
  const bottomDistance = control.bounds().bottom
  // 差值
  const diff = h - bottomDistance
  console.log('距离计算-->',h, bottomDistance ,diff, diff < BOTTOM_SAFE_DISTANCE);
  // 如果 位置差小于安全距离,执行滚动
  if (diff < BOTTOM_SAFE_DISTANCE) {
    console.log('执行滚动');
    scrollTo(-(h/6)*3)
  }
}

// 是否包含已经点过赞的控件
function isIncludeHadLikeControl(control{
  return control.find(id('com.tencent.mm:id/n6')).size()
}

// 点赞操作
function giveALike(control{
  // 判断一下是不是在可视区域
  isNeedScroll(control)
  // 判断是不是点过赞
  if (isIncludeHadLikeControl(control)) {
    return
  }
  // 打开点赞tag
  openLikeTag(control)
}

// 打开点赞的tag
function openLikeTag(control{
  control.findOne(desc('评论')).click()
  sleep(OPEN_LIKE_TAG_SLEEP)
  execute(control)
}

// 点赞
function execute({
  id('n2').findOne().click()
  sum++
  console.log('当前一共点赞:', sum)
  sleep(EXECUTE_LIKE_OVER_SLEEP)
}

function main({
  const ALL_TIME =
    SCROLL_SLEEP + OPEN_LIKE_TAG_SLEEP + EXECUTE_LIKE_OVER_SLEEP + 100
  const collection = className('android.widget.LinearLayout')
    .id('com.tencent.mm:id/ju1')
    .find()
  collection.forEach(c => {
    giveALike(c)
    sleep(ALL_TIME)
  })
}


while (sum < 8) {
  main()
}

toast('over!')

竞品

和其他框架相比,Auto.js也有着很大优势

  • 与按键精灵相比,它「无需Root」,可直接指定控件进行操作
  • 与Robotium相比,它「无需连接电脑即可运行」,代码简单,学习成本非常低
  • 控件查找方便
  • 自动生成代码
  • 社区强大

按键精灵

Auto.js 自动化工作流 JavaScript IDE
1650247929431-e413787e-3336-4250-b6fe-7ad0d5801aee
  • 手机需要root
  • 即便现在有免root打包,但原理大概是在手机上创建个虚拟空间,在空间里运行脚本,对手机性能要求高,并且部分机型不支持
  • 基于图色查找元素,存在不同机型的分辨率和颜色不同,导致脚本无法兼容。但目前可以添加jsdroid等能够解析ui组件的插件,通过ui节点来寻找元素
  • 按键精灵的语法很啰嗦,对有编程经验的人来说不适应
  • 图色查找,脚本执行速度慢

easyClick

Auto.js 自动化工作流 JavaScript IDE
1650247964361-25c591a5-49e8-47d9-a4e0-7760cec3f791
  • 社群不够强大
  • 发展较晚
  • 相比auto.js更为先进


原文始发于微信公众号(WEB大前端):Auto.js 自动化工作流 JavaScript IDE

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/225436.html

(1)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!