Auto.JS 开发

From:https://blog.csdn.net/a6892255/article/details/107302369

autojs 代码大全(实战演练):https://blog.csdn.net/qq_30931547/article/details/106459765

https://github.com/snailuncle/autojsCommonFunctions/blob/master/autojsCommonFunctions.js8

确定要控件(唯一性),以下三个方法基本可满足日常:

  • ① depth-desc-id-classname等控件的常规的唯一属性.
  • ② findone.parent().child(xx)等控件的父子依存顺序关系.
  • ③ 最后实在无法确定可用坐标法bounds,click(x,y)操作控件
     

autojs 一些问题解决的方案

怎么用其他按键强行停止脚本

events.onKeyDown("home", function (event) {//按home停止
    toast("程序结束")
    console.hide()
    threads.shutDownAll()

})

events.onKeyDown("volume_down", function (event) {//按音量下停止
    toast("程序结束")
    console.hide()
    threads.shutDownAll()

})

遇到 ui 堵塞怎么办

解决方案,将 sleep 重写为一个暂停的 function

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

扫描二维码关注公众号,回复: 13420174 查看本文章

如何方便的申请权限

function 权限申请() {
    if (!floaty.checkPermission()) {
        toast("请开启悬浮窗和后台弹出界面权限");
        floaty.requestPermission();
        return
    }
    if (auto.service == null) {
        toast("请开启脚本的无障碍服务");
        auto.waitFor();
        return
    }
}
权限申请();

怎么运行 engines.execScript(“运行脚本名字”, Source);

var url = "http://源码所在位置"//你要运行的js云端源码
var res = http.post(url, {});
var Source = res.body.string();//变量自己随便写就行
if (Source != "") {
    engines.execScript("运行脚本名字", Source);
} else {
    toast("似乎出了点问题,请联系作者解决");
}

无法安装,报毒.没有安装的入口,怎么破

一般打开设置,打开安全与隐私关闭即可。
如果是vivo 去安全中心。把应用加白名单即可。

为什么有时候 launchApp(‘应用名’); 会失效

第一,这是因为你手机存在同名软件.找不到你想要的app,要使用launch(‘应用包名’)。第二,是因为你没有给软件(后台弹出界面)权限

auto.waitFor();

语句增加是非常必要的这个在你没有无障碍时候会提示你无障碍模式的开启。并且开启之后,会接着继续运行.

AutoJs创建项目后打包成Apk后Apk无法运行

AutoJs单文件打包成Apk说是风险Apk。这个问题现在已经比较好解决了.就是使用AUTO.JS Pro版本即可,破解版可以打包.

怎么黑屏跑脚本(省电)

原理很简单,就是去创建一个黑色的悬浮窗,在oled屏幕上,就不会发光,降低功耗.兼容任何脚本

function 息屏() {
    var h = confirm("是否要进入息屏挂机模式吗?可按下音量上键停止辅助。并不能锁定屏幕!!!通知栏会正常显示(oled屏可用)");
    if (h) {
        var w = floaty.rawWindow(
            <frame gravity="center" bg="#000000" />
        );
        w.setSize(-1, -1);
        w.setTouchable(true);
        //保持脚本运行
        setInterval(() => { }, 1000);
    }
}
息屏()

按 F5 之后不是运行脚本, 而是出现调试 Node.js

解决方案如下

 然后删除除了 autojs 运行外的 F5 快捷键

如何打开控制台

帮助 -----> 切换开发者工具 ------> console

一、基础知识和使用方法

1.1 基本常识

Auto.JS作为一款基于JavaScript语言的一款脚本,基本语法和结构可通过学习JavaScript来了解。要知道,安卓的app界面就是由一堆控件组成的,这些父子控件相互嵌套,组成了我们熟悉的app界面,因此,你要执行自动化操作,那么最常用的就是先精确的找到这个控件(唯一性),再来说下一步对其进行操作,而我个人认为Auto.JS之所以方便好用,最大的一点就是查找你想要操作的控件(包括但不限于点击、设置文本等)时特别方便明了,一眼就能看懂并能迅速定位。

比如,在Auto.JS悬浮窗点击[探测]按钮(图1.1),选择[布局范围分析],之后Auto.JS会自动分析当前范围界面的控件组成,在分析探测完成后(图1.2),可以点击[查看控件信息]来查看想要操作的控件的各种属性,控件最基本也最常见的属性有:

  • id(“XXX”),
  • className(“XXX”),
  • depth(xx),
  • text(“XXX”),
  • desc(“XXX”)

 

 

 

  在这里以右上角的【⋮】按钮为例,我想要点击它,那么先查看其控件属性(图1.3),如其所示,在该控件的诸多属性里,有此控件的bounds(),id(),className(),depth(),desc()等(图1.4),这就是该按钮所有的特性,同样的,其他控件也同此控件一样,具有这些属性,我们要利用Auto.JS执行自动化操作,核心无非就是利用各种方法先找到这个控件,其次再操作这个控件。因此,学习Auto.JS的基础部分就是学习如何唯一的确定想要操作的控件。

1.2 基于控件属性确定控件

通过查看这些属性,我们不难发现有其中一项属性desc("更多选项"),很可能是不和其他控件相同的,也就是此控件属性具有唯一性,能唯一的确定此控件,这个属性就是我们要找的。因此,我们想要对其进行操作只需要:

if (desc("更多选项").exists()) {
    sleep(1000);
    desc("更多选项").findonce().click();
}

上述代码执行后,在该界面时,desc("更多选项")首先会被查找(findonce),查找后执行点击(click),从而【⋮】按钮也就被点击了。总结下无非就是我们找到了此按钮的唯一控件属性desc("更多选项")从而再对其操作,仅此而已。

另外,加上“.exist()”是为了增加程序鲁棒性,万一控件不存在或者没找到,则程序不会执行,否则对不存在(或没找到)的控件操作会抛出异常。基于此,往往要判定控件存在与否后再进行操作,而“.exist()”返回的是一个布尔值,方便后续进行判断和操作。另外一个常用的操作就是加上“while(!)”死循环等待控件出现,如:

//当"更多选项"不存在等价于此按钮还没出现
while (!desc("更多选项").exists()) {
    //继续循环等待,直到按钮出现
    sleep(1000);
}

//执行到这里说明"更多选项"存在了,对该按钮执行点击操作
desc("更多选项").findonce().click();
return;

当分析了很多控件后不难发现,控件的文本信息往往是以desc或text属性来呈现的,比如一个该按钮名为"更多选项",其控件的desc属性或text属性就是其按钮名字,确定其名字也就确定了此控件,针对此按钮具体来说,就是其desc属性是"更多选项"。(此处要说的意思就是要具体问题具体分析,文本信息不是desc就是text,如果是desc属性就操作desc,是text属性就操作text)但是,往往就有时候我们只知道包含的部分desc或text,这时候可用这四个函数来确定:

//descContains即desc包含部分信息即可匹配
if (descContains("选项").exists()) {
    //需要执行的操作
}

//descStartsWith即desc以此信息开头即可匹配
if (descStartsWith("更多").exists()) {
    //需要执行的操作
}

///同理,text也有对应的两个函数,比如一个按钮text属性为("我的学习积分")/

//textContains即text包含部分信息即可匹配
if (textContains("学习积分").exists()) {
    //需要执行的操作
}

//textStartsWith即text以此信息开头即可匹配
if (textStartsWith("我的").exists()) {
    //需要执行的操作
}

当然,更多情况是desc和text属性并不能唯一确定要操作的控件,我们往往用其他属性(常用的有className、id、depth等)一起叠加使用来尽可能的使之唯一,比如:

//往往是诸多控件属性一起才可唯一确定待操作的控件
var myObject = className(“XXX”).depth(xx).id(“XXX”).text(“XXX”).findone();
myObject.click();

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

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

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

        首先需要了解的是控件间的依存关系,还是以之前的 app 界面为例,我们这次来分析右下角的的 “+” 号按钮,和之前一样利用Auto.JS分析控件布局,我们点击右下角的 “+” 号按钮,点击 [在布局层次中查看] ,便可以查看当前控件在整个布局层次中的上下级关系(父子关系),如图1.6所示,不难发现,从上到下,左侧五颜六色的 竖棍丨就代表着其层级关系,竖棍丨越多也就越处于底层,该 “+” 号按钮控件名为 “ImageButton”,对应着拥有 8根竖棍丨,所以其是在 第八级

另外,和 “ImageButton” 平级的控件是图1.6所示的8.2—“ImageView”控件,“ImageButton” 的父控件也就是它的上级控件是图1.6所示的7.3—“android.view.View”控件,所以我们可以建立以下关系:

        8.1中 "ImageButton" 的 parent()      = 7.3中 "android.view.View"
        7.3中 "android.view.View" 的child(0) = 8.1中 "ImageButton"
        7.3中 "android.view.View" 的child(1) = 8.2中 "ImageView"
        7.3中 "android.view.View" 的parent() = 6  中 "android.view.View"
        6  中 "android.view.View" 的child(0) = 7.1中 "LinearLayout"
        6  中 "android.view.View" 的child(1) = 7.2中 "android.view.View"
        6  中 "android.view.View" 的child(2) = 7.3中 "android.view.View"

 建立了以上隶属关系,我们就可以通过唯一确定任意其中一个不变的固定控件,便可通过控件间的隶属关系来确定我们想要的控件,从而规避了我们要找的控件属性动态变化这一情况。

 

 在这里,我们假定以之前右上角的【⋮】按钮为固定控件(因为"更多选项"这几个字基本不会变化),通过【⋮】按钮来和控件间的父子关系来确定出右下角的“+”号按钮。

首先分析右上角的【⋮】按钮的层级关系,如图1.7所示,【⋮】按钮位于第10级,它的父级parent是图1.7所示的9.3—“android.support.v7.widget.LinearLayoutCompat”,9.3的父级是8—“android.view.View”,以此类推,发现我们想要操作的控件也就是“ImageButton”控件(8.1)和右上角的【⋮】按钮即“ImageView”控件(10)关系是:

        "ImageView"控件(10)是从7.1—"LinearLayout"继承下来的;
        "ImageButton"控件(8.1)是从7.3—"android.view.View"继承下来的;
        而7.1和7.3是平级关系,拥有共同的父级6—"android.view.View"

 明白了这一点,我们便可通过【】按钮(10)来确定右下角的“+”号按钮(8.1),如下所示:

//首先,通过desc属性确定出【⋮】按钮(图1.7所示的10)
//属性.findone()首先找到一个控件,再在此基础上加.parent等
var moreButton = desc("更多选项").findone();

//其次,找到【⋮】按钮和右下角“+”号按钮共同的父级parent
//在这里是一层层往上找是为了方便理解,实际操作可一步到位
var 9_3Supportv7 = moreButton.parent();                             //找到图1.7所示的9.3控件
var 8viewView = moreButton.parent().parent();                       //找到图1.7所示的8控件
var 7_1LinearLayout = moreButton.parent().parent().parent();        //找到图1.7所示的7.1控件
var 6viewView = moreButton.parent().parent().parent().parent();     //找到图1.7所示的6控件

//最后,通过共同的父级parent找到右下角“+”号按钮
//在这里是一层层往下找是为了方便理解,实际操作可一步到位
var 7_3viewView = 6viewView.child(2);                               //找到图1.7所示的7.3控件
var plusButton = 6viewView.child(2).child(0);                       //找到图1.7所示的8.1控件(也就是+号按钮)

//所以,如果一步到位,总结如下:
var plusButton = moreButton.parent().parent().parent().parent().child(2).child(0);
//再对+号按钮进行点击
plusButton.click();

 此处例子里的的两个控件【⋮】按钮(10)和右下角的“+”号按钮(8.1)之间相差了很多层,因此显着有些复杂,在实际情况中,我们往往不需要跨越这么多的层级来确定控件,一般情况下不会那么复杂。实际上,父子关系嵌套两三层往往即可确定出另一控件。最后,在这里需要注意的是,确定控件时要加上“.findone()”,此方法调用后会返回所有符合条件的控件集合。因此,首先要找到一个不容易变化的或容易找的控件“.findone()”后再在此基础上进行“.parent()”“.child(index)”操作找到另一控件。

1.4 基于坐标来确定控件

如果一个控件本身无法通过click()点击,那么我们可以利用bounds()函数获取其坐标,再利用坐标点击。总体来说,基于坐标来确定要操作的控件比较简单,核心是确定要操作的控件的坐标即可。Auto.JS里可以直接获取控件的坐标,每一个控件包含其“.bounds()”属性,bounds()其实表示的是一个范围矩阵。此处还是以右下角的“+”号按钮为例,查看控件属性信息,包含“.bounds()”属性。

 从上图看,bounds()属性是四个坐标值,其分别为(left, top, right, buttom),各值含义如图1.9所示:

        left:控件左边缘与屏幕左边的距离
        top:控件上边缘与屏幕上边的距离
        right:控件右边缘与屏幕左边的距离
        buttom : 控件下边缘与屏幕上边的距离

因此,在获得控件的坐标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)
bounds(left, right, top, bottom).clickable().click():点击该长方形区域

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

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

   当我们使用540,960分辨率的设备(x,y各缩小了一半)后执行上述代码时,他会自动计算缩放的比例,从而实际点击的是坐标(400,100)和(150,250)这两个坐标值。

当然,基于坐标的操作不仅有click()操作,常用的还有swipe()滑动操作,gesture()手势滑动操作等,具体可查看官方文档,在这里仅对swipe(),gesture()这两个函数进行介绍。
 

// (x1,y1)代表起始点坐标,(x2,y2)代表终点坐标,time代表滑动所需要的时间
swipe(x1,y1,x2,y2,time)

// 注意:安卓7以下的滑动需要root权限,且函数名变为Swipe(x1,y1,x2,y2,time)
// time同,(x1,y1)是起始点坐标,(x2,y2)是途径点坐标,最后一个坐标是终点
gesture(time,[x1,y1],[x2,y2],[x3,y3]...)

下面以一个某APP注册时的滑动验证为例(为防止被人恶意利用,在此打上码。再次声明,此处做仅举例用,所有代码仅供学习交流!),如图2.0所示,当我们输入手机号点击注册时,此时需要将滑块拖动到指定位置处才可以发送验证码从而进行下一步的注册。在这里,我们将想要拖动的滑块称为控件①,想要拖到的目标处称为控件②。那么,如果想要实现一个自动化拖动首先就要确定的是控件①和控件②的坐标,又因为其坐标每次都是随机的,所以只需要根据两个控件的特有属性唯一确定出控件①和控件②,再每次获取其坐标即可。

通过对两个控件的属性分析,如图2.1所示,我们不难发现,控件①和控件②的indexInParent()不同,因此可通过此分别唯一的定位出两个控件,确定控件后,我们再调用swipe()或gesture()函来执行滑动,从而实现自动滑动的操作。

 具体实现代码如下:

//首先判断是否进入了"滑动验证"界面
if (text("滑动验证").exists()) {
    sleep(2000);
    //判断控件1是否存在
    if (className("android.widget.Button").depth(8).indexInParent(1).exists()) {
        //控件1存在,获取其坐标bounds()属性
        var Button1 = className("android.widget.Button").depth(8).indexInParent(1).findOne().bounds();
        sleep(500);
        console.log("Button1的坐标为:" + Button1);
    }
    //判断控件2是否存在
    if (className("android.widget.Button").depth(8).indexInParent(3).exists()) {
        var Button2 = className("android.widget.Button").depth(8).indexInParent(3).findOne().bounds();
        sleep(500);
        console.log("Button2的坐标为:" + Button2);
    }

    //两个控件的坐标都获取到后,执行swipe或gesture操作
    sleep(2000);
    //swipe(x1,y1,x2,y2,[time])
    swipe(Button1.left, Button1.top, Button2.left, Button2.top, [random(500, 1200)]);
    sleep(2000);
    //gesture(time,[x1,y1],[x2,y2])
    console.log("Swipe完成!");
    gesture(random(500, 1200), [Button1.left, Button1.top], [Button2.left, Button2.top]);
    console.log("gesture完成!");
    //结束
}

二、其他的一些方法和函数



2.1 点击 click 的一些说明

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

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


//二:根据坐标来实现基于坐标的点击
setScreenMetrics(1080, 1920);   // 声明是基于分辨率1080,1920的点击
click(800, 200);                // 分辨率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开始)

2.2 查找函数 find、findone、findonce

之前说过,根据我们设定的一些属性,我们可以对屏幕上的控件进行搜索和查找,并返回符合条件的控件。因此,想要对返回的控件进行下一步操作的前提是必须要“.findone()”,如前面所述,要想从父子关系查找关联控件必须先“.findone().parent()”,再比如要想获取控件的坐标矩阵也必须“.findone().bounds()”。三个搜索函数“.find()” “.findone()” “.findonce()”在使用上是有点差别的,具体如下:

//find()函数会找出所有满足条件的控件并返回一个控件集合,之后可以对控件集合进行操作
var findAssemble = textContains("2020-07-15").find();     //找到所有包含"2020-07-15"的控件集合findAssemble 


//findone()函数会对屏幕上的控件进行搜索,直到屏幕上出现满足条件的一个控件为止,并返回该控件
//如果找不到控件,当屏幕内容发生变化时会重新寻找,直至找到
//注意:如果findone不加限制时间且屏幕上一直没有出现所描述的控件,则该函数会阻塞,直到找到为止
var findoneAssem1 = textContains("2020-07-15").findone();     //找到一个包含"2020-07-15"的控件findoneAssem1
var findoneAssem2 = textContains("2020-07-15").findone(500); //在500毫秒内找到一个包含"2020-07-15"的控件findoneAssem,若找不到,终止搜索返回null


//findonce(i)函数会根据当前所确定的筛选条件,对屏幕上的控件进行搜索,并返回第 i + 1 个符合条件的控件
//如果没有找到符合条件的控件,或者符合条件的控件个数 < i, 则返回null
var findonce1 = textContains("2020-07-15").findonce(0);     //搜索第一个包含"2020-07-15"的控件findonce1
var findonce2 = textContains("2020-07-15").findonce(1);     //搜索第二个包含"2020-07-15"的控件findonce2

2.3 上下滑动和翻页

Auto.JS上下滑动可以是对整个屏幕的滑动或对某特定控件的滑动,对整个屏幕滑动如下例2.3.1所示:

//滑动函数swipe(x1,y1,x2,y2,time)
var h = device.height;               //屏幕高
var w = device.width;                //屏幕宽
var x = (w / 3) * 2;                     //横坐标2分之3处
var h1 = (h / 6) * 5;                    //纵坐标6分之5处
var h2 = (h / 6);                      //纵坐标6分之1处

swipe(x, h1, x, h2, 500);          //向下翻页(从纵坐标6分之5处拖到纵坐标6分之1处)
swipe(x, h2, x, h1, 500);          //向上翻页(从纵坐标6分之1处拖到纵坐标6分之5处)

在很多时候,我们经常会见到className名为“.ListView”的控件,实际上其往往是充当装在很多list的集合,以此控件为例,我们可以实现对此控件的上下滑动,如下例2.3.2所示:

// scrollForward()函数会对控件执行向前滑动的操作,并返回是否操作成功
// scrollBackward()函数会对控件执行向后滑动的操作,并返回是否操作成功
var listView = ClassName(“ListView”).id(XXX).findone();
ListView.scrollForward();         // 向前滑动
ListView.scrollBackward();        // 向后滑动

 在此只是以“.ListView”的控件进行举例,实际使用过程中,只要控件是可上下滑动的,都可以调用函数“scrollForward()”“scrollBackward()”来实现对控件的滑动操作。

2.4 其他通用函数和全局函数

因为Auto.JS是基于JavaScript的语言,因此基本的语法结构、很多两者通用的函数等都是可以在JavaScript里面找到,如常见的“.replace()”“.indexof()”“.test()”等这些都在JavaScript网站上有相关的用法说明。可见:JavaScript学习:JavaScript 正则表达式 | 菜鸟教程

另外,Auto.JS的一些全局函数如启动程序函数“launch(“XXX”)”,控制台的函数如“console.show()”、“console.log()”等,再如返回函数“back()”,随机函数“random(start,end)”等这些都可在官方的提取文档里找到。可见:Auto.JS官方提取文档:【Auto.JS】Autojs官方提取文档使用说明函数 (1)_小宋小宋-CSDN博客

1. 对于文本设置函数“.setText(index,文本内容)”,如果index没有,则默认是在当前页面找所有文本框中填入“文本内容”。

2. 函数“.split("")”可用于分割字符串,比如文本“我爱你中国”会被分成“我”“爱”“你”“中”“国”,可以用在取随机姓名时百家姓的分割。

3.有些控件的“.clickable()”属性是为false的,这意味着你进行click()点击操作会返回false失败,因此,在对控件操作时要注意查看其“.clickable()”属性,如果其不可点击,我们可用一些屏幕点击或坐标点击的方法来对其进行点击。

Auto.js 一些高级用法

判断打开应用失败,未安装

打开应用("微信")
function 打开应用(应用名) {
    if (getPackageName(应用名) != null) {
        app.launchApp(应用名);
        toastLog("打开成功")
    } else {
        alert("温馨提示", "请下载安装" + 应用名);
        console.log("请下载安装" + 应用名);
        console.hide();
        console.log("关闭控制台");
        console.log("停止" + 应用名 + "脚本");
        exit();
    }
}

在线更新,在线强制更新

function 版本更新() {
    var 当前版本号 = app.versionName, 新版本号
    var url = "验证网址"
    var res = http.get(url)
    res = res.body.string()
    新版本号 = res.substr(0, 5)//截取字符,前面5位作为版本号对比
    log("当前版本号: " + 当前版本号)
    log("最新版本号: " + 新版本号)
    var 更新信息 = res.substr(5)//截取字符,第5位以后的作为公告
    log(更新信息)

    if (新版本号 != 当前版本号) {
        toastLog(更新信息)
        //自己在这里添加其他功能
        //return
        exit()

    } else {
        log("当前是最新版本!!!")
    }
}
版本更新()

判断本脚本是否重复运行

my_count = 0;
ne = engines.myEngine();
my_path = ne.mTags.get("execute_path") + "/" + ne.source;
engines.all().forEach(e => {
    path = e.mTags.get("execute_path") + "/" + e.source;
    if (path == my_path) my_count++;
});
if (my_count > 1) toast("本脚本重复运行");

在坐标位置显示一个十字架,提示2秒后关闭,方便调试

function showXyTap(x, y) {
    var xy = floaty.window(
        <frame gravity="center" w="30" h="30" alpha="0.6">
            <View w="2" h="30" bg="#ff00e4"></View>
            <View w="30" h="2" bg="#00ff42"></View>
        </frame>
    );
    xy.setPosition(Math.abs(x - 15), Math.abs(y - 15));
    setTimeout(() => {
        xy.close();
    }, 2000);
}

监控脚本是否卡在某界面不动,发现此情况重启脚本

function Observer() {
    function unique(arr) {
        let newArr = [arr[0]];
        for (let i = 1; i < arr.length; i++) {
            let flag = false;
            for (var j = 0; j < newArr.length; j++) {
                if (arr[i] == newArr[j]) {
                    flag = true;
                    break;
                }
            }
            if (!flag) {
                newArr.push(arr[i]);
            }
        }
        return newArr;
    }
    currentActis = new Array();
    for (let c = 0; c < 100; c++) {
        sleep(500);
        currentActis[c] = currentActivity();
    }
    ac = unique(currentActis);
    if (ac.length == 1) {
        return false
    }
    return true
}

// 》》》》》》》》》》》》》》》》》》》 START
work_thread = threads.start(function () {
    Main();
});


observer_thread = threads.start(function () {
    while (true) {
        sleep(5000);
        if (!Observer()) {
            work_thread.interrupt();
            work_thread = threads.start(function () {
                console.setPosition(device.width / 2, device.height / 1.5);
                console.show();
                console.warn("Main线程在5秒后重启!");
                sleep(5000);
                console.hide();
                Main();
            });
        }
    }
});

兼容安卓7.0以上与6.0以下的点击,6.0需要root

function clickHelper2(x, y) {
    showXyTap(x, y);
    if (IS_GT_SDK23) {
        click(x, y);
    } else {
        try {
            // 容易出毛病,toomanyevenliserException
            Tap(x, y);
            runtime.sleep(1000);
        } catch (e) {
            try {
                if (!ra) {
                    ra = new RootAutomator();
                }
                // 保险点击
                ra.tap(x, y, 1);
                runtime.sleep(800);
                // ra.press(x, y, 10, 1);
            } catch (e) {
                ErrorHandle("在此设备上需要Root权限才能运行本脚本!", true);
            }
        }
    }
}

跳转 到 添加QQ

app.startActivity({
    action: "android.intent.action.VIEW",
    data: "mqqapi://car/show_pslcard?&uin=542999277"
})

加群,加qq的弹窗

//测试写法qq,qq群
联系(1906507927, 553908361)
function 联系(QQ, QQ群) {
    var Q群 = QQ群
    var isFold = false,
        isRunning = false,
        isRotate = null;
    function sjcl() {
        let d = ["red", "green", "blue", "purple"]
        let y = random(0, 3)
        return d[y]

    }
    var ys = sjcl();


    var h = device.height;
    var w = device.width;
    dialogs.build({
        title: "欢迎加入更多项目群",
        titleColor: ys,
        content: "作者QQ:" + QQ + "\nautojs交流群:" + Q群,
        contentColor: ys,
        cancelable: true,
        positive: "加入Q群",
        positiveColor: ys,
        neutral: "取消",
        neutralColor: ys,
        negative: "联系作者",
        negativeColor: ys
    }).on("positive", () => {
        app.startActivity({
            action: "android.intent.action.VIEW",
            data: "mqqapi://card/show_pslcard?card_type=group&uin=" + Q群,
            packageName: "com.tencent.mobileqq",
        });
        toast("加入Q群")
    }).on("negative", () => {
        app.startActivity({
            action: "android.intent.action.VIEW",
            data: "mqqapi://card/show_pslcard?uin=" + QQ,
            packageName: "com.tencent.mobileqq",
        })

        toast("联系作者")
    }).on("neutral", () => {
        //取消键
        toast("返回")

    }).show();
}

提醒开启无障碍服务

ui.autoService.on("check", function (checked) {
    // 用户勾选无障碍服务的选项时,跳转到页面让用户去开启
    if (checked && auto.service == null) {
        app.startActivity({
            action: "android.settings.ACCESSIBILITY_SETTINGS"
        });
    }
    if (!checked && auto.service != null) {
        auto.service.disableSelf();
    }
});

跳转到开启悬浮窗

(在其它应用窗口上显示,貌似要先设置一下无障碍,才能成功跳这个)

int = app.startActivity({
    packageName: "com.android.settings",
    className: "com.android.settings.Settings$AppDrawOverlaySettingsActivity",
    data: "package:" + auto.service.getPackageName().toString()
});

结束 autojs

var nowPid = android.os.Process.myPid();
var am = context.getSystemService(java.lang.Class.forName("android.app.ActivityManager"));
var list = am.getRunningAppProcesses();
for (var i = 0; i < list.size(); i++) {
    var info = list.get(i)
    if (info.pid != nowPid) {
        kill(info.pid);
    }
}
kill(nowPid);
function kill(pid) {
    android.os.Process.killProcess(pid);
}

base64 加密和解密

function FuckYourFamily(str) {
    return java.lang.String(android.util.Base64.decode(java.lang.String(str).getBytes(), 0));
}
//调用方式,直接  FuckYourFamily(str) 即可,其中str为base64加密

适配安卓5到9.如果有root,会用root点击或者滑动.

function 滑动(x1, y1, x2, y2, time) {
    if (device.sdkInt < 24) {
        Swipe(x1, y1, x2, y2, time)
    } else {
        swipe(x1, y1, x2, y2, time)
    }
}
function 点击(x, y) {
    if (device.sdkInt < 24) {
        Tap(x, y)
    } else {
        click(x, y)
    }
}
function home() {
    if (device.sdkInt < 24) {
        Home()
    } else {
        home()
    }
}
function back() {
    if (device.sdkInt < 24) {
        Back()
    } else {
        back()
    }
}

/** * 不能点击的控件,通过坐标点击 * @param {*} uiSelector */

function clickUiBounds(ui) {
    if (ui.exists()) {
        var a = ui.findOnce();
        if (a) {
            var b = a.bounds();
            if (b) {
                click(b.centerX(), b.centerY());
                return true;
            }
        }
    }
    return false;
}

配置读写

function 写配置(文件名, 键, 值) {
    const storage = storages.create(文件名);
    storage.put(键, 值);
}
function 读配置(文件名, 键, 默认值) {
    const storage = storages.create(文件名);
    if (storage.contains(键)) {
        return storage.get(键, 默认值);
    };
    写配置(文件名, 键, 默认值);
    return 默认值;
}

文本操作

function 到文本(数值) {
    return 数值 + ""
}

function 到整数(文本) {
    return 文本 * 1
}

function 随机数(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min
}
//log(随机数(1,5))


function 子文本替换(原始文本, 欲被替换的文本, 用作替换的文本) {
    return 原始文本.replace(eval("/" + 欲被替换的文本 + "/g"), 用作替换的文本)
}
log(子文本替换("1234567890", "123", "qwe"))
function 取文本中间(str, firstStr, secondStr)
//字符串,开头,结尾
{
    if (firstStr == "") { str = "**" + str; firstStr = "**" }
    if (secondStr == "") { str = str + "**"; secondStr = "**" }
    if (str == "" || str == null || str == undefined) {
        return "";
    }
    if (str.indexOf(firstStr) < 0) {
        return "";
    }
    var subFirstStr = str.substring(str.indexOf(firstStr) + firstStr.length, str.length);
    var subSecondStr = subFirstStr.substring(0, subFirstStr.indexOf(secondStr));
    return subSecondStr;
}

判断系统是否 ROOT

function isRoot() {
    var r = shell("ls /system/bin", true).result.toString()
    if (r.length > 50) {
        return true
    } else {
        return false
    }
}

判断网络状态

function networkInfo() {
    importClass(android.net.ConnectivityManager);
    var mConnectivityManager = context.getSystemService(context.CONNECTIVITY_SERVICE);
    netInfo = mConnectivityManager.getActiveNetworkInfo();
    if (netInfo != null && netInfo.isAvailable()) {
        /网络连接
        var name = netInfo.getTypeName();
        if (netInfo.getType() == ConnectivityManager.TYPE_WIFI) {
            /WiFi网络
            return "WIFI"
        } else if (netInfo.getType() == ConnectivityManager.TYPE_ETHERNET) {
            /有线网络
            return "有线"

        } else if (netInfo.getType() == ConnectivityManager.TYPE_MOBILE) {
            /3g网络
            return "移动"

        }
        else {
            return "未知"
        }
    } else {
        网络断开
        return "断开"
    }
}

清理缓存

checkSpace()
function clickUi(ui) {
    if (ui.exists()) {
        var a = ui.findOnce();
        if (a) {
            var b = a.bounds();
            if (b) {
                click(b.centerX(), b.centerY());
                return true;
            }
        }

    }
    return false;
}
//清理缓存,如果缓存空间不足
function checkSpace() {
    if (textStartsWith('存储空间').exists() && text('取消').exists()) {
        var o = text('取消').findOnce();
        if (o) {
            clickui(o);
            sleep(1000);
        }

        if (device.sdkInt > 24) {
            var intent = new Intent();
            intent.setAction("android.settings.INTERNAL_STORAGE_SETTINGS"); //MEMORY_CARD_SETTINGS
            app.startActivity(intent);
            sleep(3000);


            var o = text('缓存数据').findOnce();    //小米
            if (o) {
                clickui(o);
                sleep(1000);
                if (text('取消').exists() && text('确定').exists()) {
                    var o = text('确定').findOnce();
                    clickui(o);
                    sleep(10000);
                }
            }

            swipe(100, 200, 100, 500, 500);//z注意,这里需要修改.这里是用于滑动的
            var o = text('垃圾清理').findOnce();    //小米
            if (o.length > 0) {
                // clickObject(o[o.length - 1]);
                o.click()
                sleep(3000);
                var iii = 10;
                while (iii-- > 0) {
                    if (textStartsWith('清理选中垃圾').exists()) {
                        var o = textStartsWith('清理选中垃圾').findOnce();
                        o.click()
                        sleep(5000);
                        back();
                        break;
                    }
                    else
                        sleep(5000);
                }
            }
        }
        else {
            app.startActivity({
                action: "android.settings.INTERNAL_STORAGE_SETTINGS",
                root: true
            });
            sleep(8000);

            var o = text('缓存数据').findOnce();    //小米
            if (o) {
                o.click();
                sleep(1000);
                if (text('取消').exists() && text('确定').exists()) {
                    var o = text('确定').findOnce();
                    clickui(o);
                    sleep(10000);
                }
            }
        }

        back();
    }
}

示例:

Auto.JS 取随机姓名

/**
 * @description: 取随机姓名函数
 * @param: null
 * @return: randomName随机姓名
 */
function randomName() {

    var allFirstName = "王李张刘陈杨黄吴赵周徐孙马朱胡林郭何高罗郑梁谢宋唐许邓冯韩曹曾彭萧蔡潘田董袁于余叶蒋杜苏魏程吕丁沈任姚卢傅钟姜崔谭廖范汪陆金石戴贾韦夏邱方侯邹熊孟秦白江阎薛尹段雷黎史龙陶贺顾毛郝龚邵万钱严赖覃洪武莫孔"
    var firstNameArray = allFirstName.split("");
    var firstNameIndex = random(0, firstNameArray.length - 1);
    var firstName = firstNameArray[firstNameIndex];


    var allSecondNames = "伟刚勇毅俊峰强军平保东文辉力明永健世广志义兴良海山仁波宁贵福生龙元全国胜学祥才发武新利清飞彬富顺信子杰涛昌成康星光天达安岩中茂进林有坚和彪博诚先敬震振壮会思群豪心邦承乐绍功松善厚庆磊民友裕河哲江超浩亮政谦亨奇固之轮翰朗伯宏言若鸣朋斌梁栋维启克伦翔旭鹏泽晨辰士以建家致树炎德行时泰盛雄琛钧冠策腾楠榕风航弘秀娟英华慧巧美娜静淑惠珠翠雅芝玉萍红娥玲芬芳燕彩春菊兰凤洁梅琳素云莲真环雪荣爱妹霞香月莺媛艳瑞凡佳嘉琼勤珍贞莉桂娣叶璧璐娅琦晶妍茜秋珊莎锦黛青倩婷姣婉娴瑾颖露瑶怡婵雁蓓纨仪荷丹蓉眉君琴蕊薇菁梦岚苑婕馨瑗琰韵融园艺咏卿聪澜纯毓悦昭冰爽琬茗羽希宁欣飘育滢馥筠柔竹霭凝晓欢霄枫芸菲寒伊亚宜可姬舒影荔枝思丽子璇淼国栋夫子瑞堂甜敏尚国贤贺祥晨涛昊轩易轩益辰益帆益冉瑾春瑾昆春齐杨文昊东东雄霖浩晨熙涵溶溶冰枫欣欣宜豪欣慧建政美欣淑慧文轩文杰欣源忠林榕润欣汝慧嘉新建建林亦菲林冰洁佳欣涵涵禹辰淳美泽惠伟洋涵越润丽翔淑华晶莹凌晶苒溪雨涵嘉怡佳毅子辰佳琪紫轩瑞辰昕蕊萌明远欣宜泽远欣怡佳怡佳惠晨茜晨璐运昊汝鑫淑君晶滢润莎榕汕佳钰佳玉晓庆一鸣语晨添池添昊雨泽雅晗雅涵清妍诗悦嘉乐晨涵天赫玥傲佳昊天昊萌萌若萌"
    var secondNameArray = allSecondNames.split("");

    var secondName = "";
    var givenNameLength = random(1, 2);
    if (givenNameLength == 1) {
        var secondNameIndex = random(0, secondNameArray.length - 1);
        secondName = secondNameArray[secondNameIndex];
    }
    if (givenNameLength == 2) {
        var secondNameIndex1 = random(0, secondNameArray.length - 1);
        var secondNameIndex2 = random(0, secondNameArray.length - 1);
        secondName = secondNameArray[secondNameIndex1] + secondNameArray[secondNameIndex2];
    }
    randomName = (firstName + secondName).toString();
    return randomName;
}

UI 用户界面设计

/********************************************UI部分***********************************************/
auto.waitFor();    //等待获取无障碍辅助权限
var form =
{
    isText: false,
}
let deviceWidth = device.width;
let deviceHeight = device.height;
let margin = parseInt(deviceWidth * 0.03);
let buttonWidth = parseInt(deviceWidth * 0.40);
ui.layout(
    <vertical margin={margin + "px"} gravity="left|top">
        <appbar>
            <toolbar id="toolbar" title="软件Title" />
        </appbar>
        <Switch id="autoService" text="无障碍服务" checked="{
   
   {auto.service != null}}" padding="8 8 8 8" textSize="15sp" />

        <text textSize="16sp" textColor="red" text="使用前请确保无障碍服务和悬浮窗权限已经开启!" />

        <vertical>
            <button id="all" h="50" text="开始运行" />
        </vertical>

        <horizontal>
            <button margin={margin + "px"} id="dq" h="40" text="单独功能1" w={buttonWidth + "px"} />
            <button margin={margin + "px"} id="cq" h="40" text="单独功能2" w={buttonWidth + "px"} />
        </horizontal>


        <horizontal>
            <button margin={margin + "px"} id="wq" h="40" text="单独功能3" w={buttonWidth + "px"} />
            <button margin={margin + "px"} id="sq" h="40" text="单独功能4" w={buttonWidth + "px"} />
        </horizontal>

        <horizontal>
            <button margin={margin + "px"} id="update" h="40" text=" 单独功能5 " w={buttonWidth + "px"} />
            <button margin={margin + "px"} id="stop" h="40" text="停止运行" w={buttonWidth + "px"} />
        </horizontal>

        <horizontal>
            <text textSize="16sp" marginLeft="15" textColor="black" text="用户参数输入:" />
            <input id="acatlog" text="" />
        </horizontal>

        <vertical>
            <text text="用户参数选择" textColor="#222222" />
            <horizontal>
                <radio id="yes_read" text="是" checked="true"></radio>
                <radio id="no_read" text="否"></radio>
            </horizontal>
        </vertical>


        <text id={"resultLabel"} textColor="red" w="*" />
        <button w="250" layout_gravity="center" id="about" text="关于本软件" />
    </vertical>
);

//创建选项菜单(右上角)
ui.emitter.on("create_options_menu", menu => {
    menu.add("启动悬浮窗");
    menu.add("运行日志");
});

//监听选项菜单点击
ui.emitter.on("options_item_selected", (e, item) => {
    switch (item.getTitle()) {
        case "启动悬浮窗":
            var intent = new Intent();
            intent.setAction("android.settings.action.MANAGE_OVERLAY_PERMISSION");
            app.startActivity(intent);
            break;
        case "运行日志":
            app.startActivity('console');
            break;
    }
    e.consumed = true;
});


ui.yes_read.on("check", function (check) {
    if (check) {
        form.isText = true;
        textTarget = true;
    }
});
ui.no_read.on("check", function (check) {
    if (check) {
        form.isText = false;
        textTarget = false;
    }
});


ui.acatlog.setText(aCatlog.toString());


ui.autoService.on("check", function (checked) {
    // 用户勾选无障碍服务的选项时,跳转到页面让用户去开启
    if (checked && auto.service == null) {
        app.startActivity({ action: "android.settings.ACCESSIBILITY_SETTINGS" });
    }
    if (!checked && auto.service != null) {
        auto.service.disableSelf();
    }
});

// 当用户回到本界面时,resume事件会被触发
ui.emitter.on("resume", function () {
    // 此时根据无障碍服务的开启情况,同步开关的状态
    ui.autoService.checked = auto.service != null;
});



var thread = null;
ui.all.click(function () {
    if (thread != null && thread.isAlive()) {
        alert("注意!", "当前程序正在运行,请结束之前进程");
        return;
    }
    toast("开始运行");
    thread = threads.start(function () {
        aCatlog = ui.acatlog.getText();
        if (auto.service == null) {
            toastLog("请先开启无障碍服务!");
            return;
        }
        main();   //主函数开始运行
    });
});


ui.cq.click(function () {
    if (thread != null && thread.isAlive()) {
        alert("注意!", "当前程序正在运行,请结束之前进程");
        return;
    }
    thread = threads.start(function () {
        if (auto.service == null) {
            toastLog("请先开启无障碍服务!");
            return;
        }
        //此处开始执行函数();
    });
});


ui.dq.click(function () {
    if (thread != null && thread.isAlive()) {
        alert("注意!", "当前程序正在运行,请结束之前进程");
        return;
    }
    thread = threads.start(function () {
        if (auto.service == null) {
            toastLog("请先开启无障碍服务!");
            return;
        }
        //此处开始执行函数();
    });
});


ui.wq.click(function () {
    if (thread != null && thread.isAlive()) {
        alert("注意!", "当前程序正在运行,请结束之前进程");
        return;
    }
    thread = threads.start(function () {
        if (auto.service == null) {
            toastLog("请先开启无障碍服务!");
            return;
        }
        //此处开始执行函数();
    });
});

ui.sq.click(function () {
    if (thread != null && thread.isAlive()) {
        alert("注意!", "当前程序正在运行,请结束之前进程");
        return;
    }
    thread = threads.start(function () {
        if (auto.service == null) {
            toastLog("请先开启无障碍服务!");
            return;
        }
        //此处开始执行函数();
    });
});



ui.stop.click(function () {
    if (thread != null && thread.isAlive()) {
        threads.shutDownAll();
        toast("已停止运行!")
        console.hide();
    }
    else {
        toast("当前没有线程在运行!")
    }
});


ui.about.click(function () {
    alert("说明", "本文中所有代码和举例均为学习交流之用,不得用于其他用途!");
});

猜你喜欢

转载自blog.csdn.net/freeking101/article/details/121586713