注册

模拟点击与群控——autojs使用

写在前面


autojs是利用accessibility功能实现的一套免root自动化模拟点击框架,让开发者可以直接通过使用js脚本实现一系列的自动化操作,包括:触摸屏幕、滑动、输入文字等。autojs具有很高的灵活性和可扩展性,可以被用于各种场景,例如自动化游戏操作、自动登录、自动化测试等。


autojs免费版本已经停止维护,官网只有Autojs Pro付费版本。然而最近(20230214)又因为一些合规问题,被强制下架已经不允许用户注册。市面上可使用代替产品autox,该项目基于原autojs4.1版本基础上进行维护开发。本文主要围绕该项目展开。


开发环境搭建


使用vscode,添加插件Auto.js-Autox.js-VSCodeExt,如下图,注意不要重复安装多个类似插件,会存在冲突问题。




按住cmd+shift+p,输入> Auto.js,选择"开启服务(Start Server)",此时右下角会提示服务正在运行提示,并显示ip地址信息,如下图,ip为:192.168.1.102




手机与电脑连接同个wifi,并打开autox app,打开"无障碍服务",并打开"连接电脑"按钮,输入ip地址,如下图,点击确认,即可与电脑同步。



连接成功后出现如下提示,此时开发环境搭建完成。



测试hello world程序,创建Autox.js文件,并输入内容toast("hello world!"),选择js文件,右键-重新运行,即可将脚本同步到手机运行,此时手机会出现hello world!的一个toast提示。



js脚本开发指导


关于autojs的API可参考官方文档,这里主要是讲解一下使用的思路。我们在开发自动化工具时,最常见的问题就是如何找到我们所需要点击的控件节点,每一个节点包含的信息包括:



  • className 类名。类名表示一个控件的类型,例如文本控件为"android.widget.TextView",图片控件为"android.widget.ImageView"等。
  • id控件节点的唯一id。
  • text节点名字,不一定有,可能为空。
  • desc节点的描述信息,不一定有,可能为空。
  • packageName 包名。包名表示控件所在的应用包名,例如 QQ 界面的控件的包名为"com.tencent.mobileqq"。
  • bounds 控件在屏幕上的范围。
  • drawingOrder 控件在父控件的绘制顺序。
  • indexInParent 控件在父控件的位置。
  • clickable 控件是否可点击。
  • longClickable 控件是否可长按。
  • checkable 控件是否可勾选。
  • checked 控件是否可以勾选。
  • scrollable 控件是否可滑动。
  • selected 控件是否已选择。
  • editable 控件是否可编辑。
  • visibleToUser 控件是否可见。
  • enabled 控件是否已启用。
  • depth 控件的布局深度。

控件id是最为常用的一个唯一性标记,我们写自动化认为时,经常使用id来对特定控件做点击操作。但是我们如何得知具体控件id信息呢?我们可以利用以下js脚本,将整个界面的控件信息进行打印输出。


toastLog("start.");

function printNode(node){
if(node){
var text = node.text();
var desc = node.desc();
let bounds = node.bounds();
let left = bounds.left;
let top = bounds.top;
let right = bounds.right;
let bottom = bounds.bottom;
var click = node.clickable();
var id = node.id();
log(id, text, desc, click, left, right, top, bottom);
}
}

function traverse(node) {
printNode(node);
var cnt = node.childCount();
for (var i = 0; i < cnt; i++) {
traverse(node.child(i));
}
}

let windowRoot = auto.rootInActiveWindow;
if(windowRoot){
log("tracerse node.");
traverse(windowRoot);
}else{
log("window root is null.");
}

我们可以结合node.bounds()中控件的大小以及所在位置,来猜测我们所要点击的目标控件。在获得某个具体控件id后,即可使用如下js脚本进行点击操作。


target_id=""
id(target_id).findOne().click()

查看viewid脚本开发


这一节我们将利用canvas绘图将每个控件绘制出来,让我们方便地看出来我们所要操作的控件viewid。首先我们需要利用递归方式遍历当前页面上的所有控件,并存放在list变量中,如下。


function traverse(node) {
if(node != null){
viewNodes.push(node);
}
var cnt = node.childCount();
for (var i = 0; i < cnt; i++) {
traverse(node.child(i));
}
}

//x:946, y:80
let windowRoot = auto.rootInActiveWindow;

if(windowRoot){
log("tracerse node.");
traverse(windowRoot); // 开始遍历控件树并打印控件的text属性
}else{
log("window root is null.");
}

function printNode(i, node){
if(node){
var text = node.text();
var desc = node.desc();
let bounds = node.bounds();
let left = bounds.left;
let top = bounds.top;
let right = bounds.right;
let bottom = bounds.bottom;
var click = node.clickable();
var id = node.id();
log(i, id, text, desc, click, left, right, top, bottom);
}
}
var len = viewNodes.length;
for (var i = 0; i < len; i++) {
let childViewNode = viewNodes[i];
printNode(i, childViewNode);
}

使用浮窗功能,在顶层绘制一张透明的画布,如下:


//ui布局为一块画布
var w = floaty.rawWindow(
<frame gravity="center" bg="#ffffff">
<canvas id="canvas" layout_weight="1"/>
</frame>
);

w.setSize(device.width, device.height); // 设置窗口大小
w.setTouchable(false); // 设置触摸透传

使用canvas绘图库,用绿色边框将各个控件圈出,并在每个控件上显示在list中对应的序号。


let paint = new Paint();
paint.setColor(colors.parseColor("#00ff00"));
paint.setStrokeWidth(5);
paint.setStyle(Paint.Style.STROKE);

let paintText = new Paint();
paintText.setColor(colors.parseColor("#FF0000"));
paintText.setTextSize(80);
paintText.setStrokeWidth(20);

var isDraw = 1;
w.canvas.on("draw", function (canvas) {
if(isDraw < 20){
isDraw = isDraw + 1;
var len = viewNodes.length;
for (var i = 0; i < len; i++) {
let childViewNode = viewNodes[i];
let bounds = childViewNode.bounds();
let left = bounds.left;
let top = bounds.top;
let right = bounds.right;
let bottom = bounds.bottom;
canvas.drawRect(left, top, right, bottom, paint);
// log(left, bottom, right, top)
canvas.drawText("" + i, left, bottom, paintText);
}
}
});

为了不让脚本退出,我们需要使用设置等待时间,让脚本持续运行,如下,若没有等待执行,脚本执行后立马退出,我们将无法看到绘图内容。


setTimeout(()=>{
w.close();
}, 50000);

效果图如下:



作者:风铃Cipher
链接:https://juejin.cn/post/7224063449616236605
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册