【Auto.JS】Autojs官方提取文档使用说明函数 (2)

接上一篇文章:Autojs官方提取文档使用说明函数 (1)

 

 

Images

Stability: 2 - Stable

images模块提供了一些手机设备中常见的图片处理函数,包括截图、读写图片、图片剪裁、旋转、二值化、找色找图等。

该模块分为两个部分,找图找色部分和图片处理部分。

需要注意的是,image对象创建后尽量在不使用时进行回收,同时避免循环创建大量图片。因为图片是一种占用内存比较大的资源,尽管Auto.js通过各种方式(比如图片缓存机制、垃圾回收时回收图片、脚本结束时回收所有图片)尽量降低图片资源的泄漏和内存占用,但是糟糕的代码仍然可以占用大量内存。

Image对象通过调用recycle()函数来回收。例如:

// 读取图片

var img = images.read("./1.png");

//对图片进行操作

...

// 回收图片

img.recycle();

例外的是,caputerScreen()返回的图片不需要回收。

图片处理

images.read(path)

  • path {string} 图片路径

读取在路径path的图片文件并返回一个Image对象。如果文件不存在或者文件无法解码则返回null。

images.load(url)

  • url {string} 图片URL地址

加载在地址URL的网络图片并返回一个Image对象。如果地址不存在或者图片无法解码则返回null。

images.copy(img)

  • img {Image} 图片
  • 返回 {Image}

复制一张图片并返回新的副本。该函数会完全复制img对象的数据。

images.save(image, path[, format = "png", quality = 100])

  • image {Image} 图片
  • path {string} 路径
  • format {string} 图片格式,可选的值为:
    • png
    • jpeg/jpg
    • webp
  • quality {number} 图片质量,为0~100的整数值

把图片image以PNG格式保存到path中。如果文件不存在会被创建;文件存在会被覆盖。

//把图片压缩为原来的一半质量并保存

var img = images.read("/sdcard/1.png");

images.save(img, "/sdcard/1.jpg", "jpg", 50);

app.viewFile("/sdcard/1.jpg");

images.fromBase64(base64)

  • base64 {string} 图片的Base64数据
  • 返回 {Image}

解码Base64数据并返回解码后的图片Image对象。如果base64无法解码则返回null。

images.toBase64(img[, format = "png", quality = 100])

  • image {image} 图片
  • format {string} 图片格式,可选的值为:
    • png
    • jpeg/jpg
    • webp
  • quality {number} 图片质量,为0~100的整数值
  • 返回 {string}

把图片编码为base64数据并返回。

images.fromBytes(bytes)

  • bytes {byte[]} 字节数组

解码字节数组bytes并返回解码后的图片Image对象。如果bytes无法解码则返回null。

images.toBytes(img[, format = "png", quality = 100])

  • image {image} 图片
  • format {string} 图片格式,可选的值为:
    • png
    • jpeg/jpg
    • webp
  • quality {number} 图片质量,为0~100的整数值
  • 返回 {byte[]}

把图片编码为字节数组并返回。

images.clip(img, x, y, w, h)

  • img {Image} 图片
  • x {number} 剪切区域的左上角横坐标
  • y {number} 剪切区域的左上角纵坐标
  • w {number} 剪切区域的宽度
  • h {number} 剪切区域的高度
  • 返回 {Image}

从图片img的位置(x, y)处剪切大小为w * h的区域,并返回该剪切区域的新图片。

var src = images.read("/sdcard/1.png");

var clip = images.clip(src, 100, 100, 400, 400);

images.save(clip, "/sdcard/clip.png");

images.resize(img, size[, interpolation])

[v4.1.0新增]

  • img {Image} 图片
  • size {Array} 两个元素的数组[w, h],分别表示宽度和高度;如果只有一个元素,则宽度和高度相等
  • interpolation {string} 插值方法,可选,默认为"LINEAR"(线性插值),可选的值有:
    • NEAREST 最近邻插值
    • LINEAR 线性插值(默认)
    • AREA 区域插值
    • CUBIC 三次样条插值
    • LANCZOS4 Lanczos插值 参见InterpolationFlags
  • 返回 {Image}

调整图片大小,并返回调整后的图片。例如把图片放缩为200*300:images.resize(img, [200, 300])。

参见Imgproc.resize

images.scale(img, fx, fy[, interpolation])

[v4.1.0新增]

  • img {Image} 图片
  • fx {number} 宽度放缩倍数
  • fy {number} 高度放缩倍数
  • interpolation {string} 插值方法,可选,默认为"LINEAR"(线性插值),可选的值有:
    • NEAREST 最近邻插值
    • LINEAR 线性插值(默认)
    • AREA 区域插值
    • CUBIC 三次样条插值
    • LANCZOS4 Lanczos插值 参见InterpolationFlags
  • 返回 {Image}

放缩图片,并返回放缩后的图片。例如把图片变成原来的一半:images.scale(img, 0.5, 0.5)。

参见Imgproc.resize

images.rotate(img, degress[, x, y])

[v4.1.0新增]

  • img {Image} 图片
  • degress {number} 旋转角度。
  • x {number} 旋转中心x坐标,默认为图片中点
  • y {number} 旋转中心y坐标,默认为图片中点
  • 返回 {Image}

将图片逆时针旋转degress度,返回旋转后的图片对象。

例如逆时针旋转90度为images.rotate(img, 90)。

images.concat(img1, image2[, direction])

[v4.1.0新增]

  • img1 {Image} 图片1
  • img2 {Image} 图片2
  • direction {string} 连接方向,默认为"RIGHT",可选的值有:
    • LEFT 将图片2接到图片1左边
    • RIGHT 将图片2接到图片1右边
    • TOP 将图片2接到图片1上边
    • BOTTOM 将图片2接到图片1下边
  • 返回 {Image}

连接两张图片,并返回连接后的图像。如果两张图片大小不一致,小的那张将适当居中。

images.grayscale(img)

[v4.1.0新增]

  • img {Image} 图片
  • 返回 {Image}

灰度化图片,并返回灰度化后的图片。

image.threshold(img, threshold, maxVal[, type])

[v4.1.0新增]

  • img {Image} 图片
  • threshold {number} 阈值
  • maxVal {number} 最大值
  • type {string} 阈值化类型,默认为"BINARY",参见ThresholdTypes, 可选的值:
    • BINARY
    • BINARY_INV
    • TRUNC
    • TOZERO
    • TOZERO_INV
    • OTSU
    • TRIANGLE
  • 返回 {Image}

将图片阈值化,并返回处理后的图像。可以用这个函数进行图片二值化。例如:images.threshold(img, 100, 255, "BINARY"),这个代码将图片中大于100的值全部变成255,其余变成0,从而达到二值化的效果。如果img是一张灰度化图片,这个代码将会得到一张黑白图片。

可以参考有关博客(比如threshold函数的使用)或者OpenCV文档threshold

images.adaptiveThreshold(img, maxValue, adaptiveMethod, thresholdType, blockSize, C)

[v4.1.0新增]

  • img {Image} 图片
  • maxValue {number} 最大值
  • adaptiveMethod {string} 在一个邻域内计算阈值所采用的算法,可选的值有:
    • MEAN_C 计算出领域的平均值再减去参数C的值
    • GAUSSIAN_C 计算出领域的高斯均值再减去参数C的值
  • thresholdType {string} 阈值化类型,可选的值有:
    • BINARY
    • BINARY_INV
  • blockSize {number} 邻域块大小
  • C {number} 偏移值调整量
  • 返回 {Image}

对图片进行自适应阈值化处理,并返回处理后的图像。

可以参考有关博客(比如threshold与adaptiveThreshold)或者OpenCV文档adaptiveThreshold

images.cvtColor(img, code[, dstCn])

[v4.1.0新增]

  • img {Image} 图片
  • code {string} 颜色空间转换的类型,可选的值有一共有205个(参见ColorConversionCodes),这里只列出几个:
    • BGR2GRAY BGR转换为灰度
    • BGR2HSV BGR转换为HSV
    • ``
  • dstCn {number} 目标图像的颜色通道数量,如果不填写则根据其他参数自动决定。
  • 返回 {Image}

对图像进行颜色空间转换,并返回转换后的图像。

可以参考有关博客(比如颜色空间转换)或者OpenCV文档cvtColor

images.inRange(img, lowerBound, upperBound)

[v4.1.0新增]

  • img {Image} 图片
  • lowerBound {string} | {number} 颜色下界
  • upperBound {string} | {number} 颜色下界
  • 返回 {Image}

将图片二值化,在lowerBound~upperBound范围以外的颜色都变成0,在范围以内的颜色都变成255。

例如images.inRange(img, "#000000", "#222222")。

images.interval(img, color, interval)

[v4.1.0新增]

  • img {Image} 图片
  • color {string} | {number} 颜色值
  • interval {number} 每个通道的范围间隔
  • 返回 {Image}

将图片二值化,在color-interval ~ color+interval范围以外的颜色都变成0,在范围以内的颜色都变成255。这里对color的加减是对每个通道而言的。

例如images.interval(img, "#888888", 16),每个通道的颜色值均为0x88,加减16后的范围是[0x78, 0x98],因此这个代码将把#787878~#989898的颜色变成#FFFFFF,而把这个范围以外的变成#000000。

images.blur(img, size[, anchor, type])

[v4.1.0新增]

  • img {Image} 图片
  • size {Array} 定义滤波器的大小,如[3, 3]
  • anchor {Array} 指定锚点位置(被平滑点),默认为图像中心
  • type {string} 推断边缘像素类型,默认为"DEFAULT",可选的值有:
    • CONSTANT iiiiii|abcdefgh|iiiiiii with some specified i
    • REPLICATE aaaaaa|abcdefgh|hhhhhhh
    • REFLECT fedcba|abcdefgh|hgfedcb
    • WRAP cdefgh|abcdefgh|abcdefg
    • REFLECT_101 gfedcb|abcdefgh|gfedcba
    • TRANSPARENT uvwxyz|abcdefgh|ijklmno
    • REFLECT101 same as BORDER_REFLECT_101
    • DEFAULT same as BORDER_REFLECT_101
    • ISOLATED do not look outside of ROI
  • 返回 {Image}

对图像进行模糊(平滑处理),返回处理后的图像。

可以参考有关博客(比如实现图像平滑处理)或者OpenCV文档blur

images.medianBlur(img, size)

[v4.1.0新增]

  • img {Image} 图片
  • size {Array} 定义滤波器的大小,如[3, 3]
  • 返回 {Image}

对图像进行中值滤波,返回处理后的图像。

可以参考有关博客(比如实现图像平滑处理)或者OpenCV文档blur

images.gaussianBlur(img, size[, sigmaX, sigmaY, type])

[v4.1.0新增]

  • img {Image} 图片
  • size {Array} 定义滤波器的大小,如[3, 3]
  • sigmaX {number} x方向的标准方差,不填写则自动计算
  • sigmaY {number} y方向的标准方差,不填写则自动计算
  • type {string} 推断边缘像素类型,默认为"DEFAULT",参见images.blur
  • 返回 {Image}

对图像进行高斯模糊,返回处理后的图像。

可以参考有关博客(比如实现图像平滑处理)或者OpenCV文档GaussianBlur

images.matToImage(mat)

[v4.1.0新增]

  • mat {Mat} OpenCV的Mat对象
  • 返回 {Image}

把Mat对象转换为Image对象。

找图找色

images.requestScreenCapture([landscape])

  • landscape {boolean} 布尔值, 表示将要执行的截屏是否为横屏。如果landscape为false, 则表示竖屏截图; true为横屏截图。

向系统申请屏幕截图权限,返回是否请求成功。

第一次使用该函数会弹出截图权限请求,建议选择“总是允许”。

这个函数只是申请截图权限,并不会真正执行截图,真正的截图函数是captureScreen()。

该函数在截图脚本中只需执行一次,而无需每次调用captureScreen()都调用一次。

如果不指定landscape值,则截图方向由当前设备屏幕方向决定,因此务必注意执行该函数时的屏幕方向。

建议在本软件界面运行该函数,在其他软件界面运行时容易出现一闪而过的黑屏现象。

示例:

//请求截图

if(!requestScreenCapture()){

    toast("请求截图失败");

    exit();

}

//连续截图10张图片(间隔1秒)并保存到存储卡目录

for(var i = 0; i < 10; i++){

    captureScreen("/sdcard/screencapture" + i + ".png");

    sleep(1000);

}

该函数也可以作为全局函数使用。

images.captureScreen()

截取当前屏幕并返回一个Image对象。

没有截图权限时执行该函数会抛出SecurityException。

该函数不会返回null,两次调用可能返回相同的Image对象。这是因为设备截图的更新需要一定的时间,短时间内(一般来说是16ms)连续调用则会返回同一张截图。

截图需要转换为Bitmap格式,从而该函数执行需要一定的时间(0~20ms)。

另外在requestScreenCapture()执行成功后需要一定时间后才有截图可用,因此如果立即调用captureScreen(),会等待一定时间后(一般为几百ms)才返回截图。

例子:

//请求横屏截图

requestScreenCapture(true);

//截图

var img = captureScreen();

//获取在点(100, 100)的颜色值

var color = images.pixel(img, 100, 100);

//显示该颜色值

toast(colors.toString(color));

该函数也可以作为全局函数使用。

images.captureScreen(path)

  • path {string} 截图保存路径

截取当前屏幕并以PNG格式保存到path中。如果文件不存在会被创建;文件存在会被覆盖。

该函数不会返回任何值。该函数也可以作为全局函数使用。

images.pixel(image, x, y)

  • image {Image} 图片
  • x {number} 要获取的像素的横坐标。
  • y {number} 要获取的像素的纵坐标。

返回图片image在点(x, y)处的像素的ARGB值。

该值的格式为0xAARRGGBB,是一个"32位整数"(虽然JavaScript中并不区分整数类型和其他数值类型)。

坐标系以图片左上角为原点。以图片左侧边为y轴,上侧边为x轴。

images.findColor(image, color, options)

  • image {Image} 图片
  • color {number} | {string} 要寻找的颜色的RGB值。如果是一个整数,则以0xRRGGBB的形式代表RGB值(A通道会被忽略);如果是字符串,则以"#RRGGBB"代表其RGB值。
  • options {Object} 选项

在图片中寻找颜色color。找到时返回找到的点Point,找不到时返回null。

选项包括:

  • region {Array} 找色区域。是一个两个或四个元素的数组。(region[0], region[1])表示找色区域的左上角;region[2]*region[3]表示找色区域的宽高。如果只有region只有两个元素,则找色区域为(region[0], region[1])到屏幕右下角。如果不指定region选项,则找色区域为整张图片。
  • threshold {number} 找色时颜色相似度的临界值,范围为0~255(越小越相似,0为颜色相等,255为任何颜色都能匹配)。默认为4。threshold和浮点数相似度(0.0~1.0)的换算为 similarity = (255 - threshold) / 255.

该函数也可以作为全局函数使用。

一个循环找色的例子如下:

requestScreenCapture();

 

//循环找色,找到红色(#ff0000)时停止并报告坐标

while(true){

    var img = captureScreen();

    var point = findColor(img, "#ff0000");

    if(point){

        toast("找到红色,坐标为(" + point.x + ", " + point.y + ")");

    }

}

一个区域找色的例子如下:

//读取本地图片/sdcard/1.png

var img = images.read("/sdcard/1.png");

//判断图片是否加载成功

if(!img){

    toast("没有该图片");

    exit();

}

//在该图片中找色,指定找色区域为在位置(400, 500)的宽为300长为200的区域,指定找色临界值为4

var point = findColor(img, "#00ff00", {

     region: [400, 500, 300, 200],

     threshold: 4

 });

if(point){

    toast("找到啦:" + point);

}else{

    toast("没找到");

}

images.findColorInRegion(img, color, x, y[, width, height, threshold])

区域找色的简便方法。

相当于

images.findColor(img, color, {

     region: [x, y, width, height],

     threshold: threshold

});

该函数也可以作为全局函数使用。

images.findColorEquals(img, color[, x, y, width, height])

  • img {Image} 图片
  • color {number} | {string} 要寻找的颜色
  • x {number} 找色区域的左上角横坐标
  • y {number} 找色区域的左上角纵坐标
  • width {number} 找色区域的宽度
  • height {number} 找色区域的高度
  • 返回 {Point}

在图片img指定区域中找到颜色和color完全相等的某个点,并返回该点的左边;如果没有找到,则返回null。

找色区域通过x, y, width, height指定,如果不指定找色区域,则在整张图片中寻找。

该函数也可以作为全局函数使用。

示例: (通过找QQ红点的颜色来判断是否有未读消息)

requestScreenCapture();

launchApp("QQ");

sleep(1200);

var p = findColorEquals(captureScreen(), "#f64d30");

if(p){

    toast("有未读消息");

}else{

    toast("没有未读消息");

}

images.findMultiColors(img, firstColor, colors[, options])

  • img {Image} 要找色的图片
  • firstColor {number} | {string} 第一个点的颜色
  • colors {Array} 表示剩下的点相对于第一个点的位置和颜色的数组,数组的每个元素为[x, y, color]
  • options {Object} 选项,包括:
    • region {Array} 找色区域。是一个两个或四个元素的数组。(region[0], region[1])表示找色区域的左上角;region[2]*region[3]表示找色区域的宽高。如果只有region只有两个元素,则找色区域为(region[0], region[1])到屏幕右下角。如果不指定region选项,则找色区域为整张图片。
    • threshold {number} 找色时颜色相似度的临界值,范围为0~255(越小越相似,0为颜色相等,255为任何颜色都能匹配)。默认为4。threshold和浮点数相似度(0.0~1.0)的换算为 similarity = (255 - threshold) / 255.

多点找色,类似于按键精灵的多点找色,其过程如下:

  • 在图片img中找到颜色firstColor的位置(x0, y0)
  • 对于数组colors的每个元素[x, y, color],检查图片img在位置(x + x0, y + y0)上的像素是否是颜色color,是的话返回(x0, y0),否则继续寻找firstColor的位置,重新执行第1步
  • 整张图片都找不到时返回null

例如,对于代码images.findMultiColors(img, "#123456", [[10, 20, "#ffffff"], [30, 40, "#000000"]]),假设图片在(100, 200)的位置的颜色为#123456, 这时如果(110, 220)的位置的颜色为#fffff且(130, 240)的位置的颜色为#000000,则函数返回点(100, 200)。

如果要指定找色区域,则在options中指定,例如:

var p = images.findMultiColors(img, "#123456", [[10, 20, "#ffffff"], [30, 40, "#000000"]], {

    region: [0, 960, 1080, 960]

});

images.detectsColor(image, color, x, y[, threshold = 16, algorithm = "diff"])

  • image {Image} 图片
  • color {number} | {string} 要检测的颜色
  • x {number} 要检测的位置横坐标
  • y {number} 要检测的位置纵坐标
  • threshold {number} 颜色相似度临界值,默认为16。取值范围为0~255。
  • algorithm {string} 颜色匹配算法,包括:
    • "equal": 相等匹配,只有与给定颜色color完全相等时才匹配。
    • "diff": 差值匹配。与给定颜色的R、G、B差的绝对值之和小于threshold时匹配。
    • "rgb": rgb欧拉距离相似度。与给定颜色color的rgb欧拉距离小于等于threshold时匹配。
    • "rgb+": 加权rgb欧拉距离匹配(LAB Delta E)。
    • "hs": hs欧拉距离匹配。hs为HSV空间的色调值。

返回图片image在位置(x, y)处是否匹配到颜色color。用于检测图片中某个位置是否是特定颜色。

一个判断微博客户端的某个微博是否被点赞过的例子:

requestScreenCapture();

//找到点赞控件

var like = id("ly_feed_like_icon").findOne();

//获取该控件中点坐标

var x = like.bounds().centerX();

var y = like.bounds().centerY();

//截图

var img = captureScreen();

//判断在该坐标的颜色是否为橙红色

if(images.detectsColor(img, "#fed9a8", x, y)){

    //是的话则已经是点赞过的了,不做任何动作

}else{

    //否则点击点赞按钮

    like.click();

}

images.findImage(img, template[, options])

  • img {Image} 大图片
  • template {Image} 小图片(模板)
  • options {Object} 找图选项

找图。在大图片img中查找小图片template的位置(模块匹配),找到时返回位置坐标(Point),找不到时返回null。

选项包括:

  • threshold {number} 图片相似度。取值范围为0~1的浮点数。默认值为0.9。
  • region {Array} 找图区域。参见findColor函数关于region的说明。
  • level {number} 一般而言不必修改此参数。不加此参数时该参数会根据图片大小自动调整。找图算法是采用图像金字塔进行的, level参数表示金字塔的层次, level越大可能带来越高的找图效率,但也可能造成找图失败(图片因过度缩小而无法分辨)或返回错误位置。因此,除非您清楚该参数的意义并需要进行性能调优,否则不需要用到该参数。

该函数也可以作为全局函数使用。

一个最简单的找图例子如下:

var img = images.read("/sdcard/大图.png");

var templ = images.read("/sdcard/小图.png");

var p = findImage(img, templ);

if(p){

    toast("找到啦:" + p);

}else{

    toast("没找到");

}

稍微复杂点的区域找图例子如下:

auto();

requestScreenCapture();

var wx = images.read("/sdcard/微信图标.png");

//返回桌面

home();

//截图并找图

var p = findImage(captureScreen(), wx, {

    region: [0, 50],

    threshold: 0.8

});

if(p){

    toast("在桌面找到了微信图标啦: " + p);

}else{

    toast("在桌面没有找到微信图标");

}

images.findImageInRegion(img, template, x, y[, width, height, threshold])

区域找图的简便方法。相当于:

images.findImage(img, template, {

    region: [x, y, width, height],

    threshold: threshold

})

该函数也可以作为全局函数使用。

images.matchTemplate(img, template, options)

[v4.1.0新增]

  • img {Image} 大图片
  • template {Image} 小图片(模板)
  • options {Object} 找图选项:
    • threshold {number} 图片相似度。取值范围为0~1的浮点数。默认值为0.9。
    • region {Array} 找图区域。参见findColor函数关于region的说明。
    • max {number} 找图结果最大数量,默认为5
    • level {number} 一般而言不必修改此参数。不加此参数时该参数会根据图片大小自动调整。找图算法是采用图像金字塔进行的, level参数表示金字塔的层次, level越大可能带来越高的找图效率,但也可能造成找图失败(图片因过度缩小而无法分辨)或返回错误位置。因此,除非您清楚该参数的意义并需要进行性能调优,否则不需要用到该参数。
  • 返回 {MatchingResult}

在大图片中搜索小图片,并返回搜索结果MatchingResult。该函数可以用于找图时找出多个位置,可以通过max参数控制最大的结果数量。也可以对匹配结果进行排序、求最值等操作。

MatchingResult

[v4.1.0新增]

matches

  • {Array} 匹配结果的数组。

数组的元素是一个Match对象:

  • point {Point} 匹配位置
  • similarity {number} 相似度

例如:

var result = images.matchTemplate(img, template, {

    max: 100

});

result.matches.forEach(match => {

    log("point = " + match.point + ", similarity = " + match.similarity);

});

points

  • {Array} 匹配位置的数组。

first()

  • 返回 {Match}

第一个匹配结果。如果没有任何匹配,则返回null。

last()

  • 返回 {Match}

最后一个匹配结果。如果没有任何匹配,则返回null。

leftmost()

  • 返回 {Match}

位于大图片最左边的匹配结果。如果没有任何匹配,则返回null。

topmost()

  • 返回 {Match}

位于大图片最上边的匹配结果。如果没有任何匹配,则返回null。

rightmost()

  • 返回 {Match}

位于大图片最右边的匹配结果。如果没有任何匹配,则返回null。

bottommost()

  • 返回 {Match}

位于大图片最下边的匹配结果。如果没有任何匹配,则返回null。

best()

  • 返回 {Match}

相似度最高的匹配结果。如果没有任何匹配,则返回null。

worst()

  • 返回 {Match}

相似度最低的匹配结果。如果没有任何匹配,则返回null。

sortBy(cmp)

  • cmp {Function}|{string} 比较函数,或者是一个字符串表示排序方向。例如"left"表示将匹配结果按匹配位置从左往右排序、"top"表示将匹配结果按匹配位置从上往下排序,"left-top"表示将匹配结果按匹配位置从左往右、从上往下排序。方向包括left(左), top (上), right (右), bottom(下)。
  • {MatchingResult}

对匹配结果进行排序,并返回排序后的结果。

var result = images.matchTemplate(img, template, {

    max: 100

});

log(result.sortBy("top-right"));

Image

表示一张图片,可以是截图的图片,或者本地读取的图片,或者从网络获取的图片。

Image.getWidth()

返回以像素为单位图片宽度。

Image.getHeight()

返回以像素为单位的图片高度。

Image.saveTo(path)

  • path {string} 路径

把图片保存到路径path。(如果文件存在则覆盖)

Image.pixel(x, y)

  • x {number} 横坐标
  • y {number} 纵坐标

返回图片image在点(x, y)处的像素的ARGB值。

该值的格式为0xAARRGGBB,是一个"32位整数"(虽然JavaScript中并不区分整数类型和其他数值类型)。

坐标系以图片左上角为原点。以图片左侧边为y轴,上侧边为x轴。

##

Point

findColor, findImage返回的对象。表示一个点(坐标)。

Point.x

横坐标。

Point.y

纵坐标。

Canvas

canvas提供了使用画布进行2D画图的支持,可用于简单的小游戏开发或者图片编辑。使用canvas可以轻松地在一张图片或一个界面上绘制各种线与图形。

canvas的坐标系为平面直角坐标系,以屏幕左上角为原点,屏幕上边为x轴正方向,屏幕左边为y轴正方向。例如分辨率为1920*1080的屏幕上,画一条从屏幕左上角到屏幕右下角的线段为:

canvas.drawLine(0, 0, 1080, 1920, paint);

canvas的绘制依赖于画笔Paint, 通过设置画笔的粗细、颜色、填充等可以改变绘制出来的图形。例如绘制一个红色实心正方形为:

var paint = new Paint();

//设置画笔为填充,则绘制出来的图形都是实心的

paint.setStyle(Paint.STYLE.FILL);

//设置画笔颜色为红色

paint.setColor(colors.RED);

//绘制一个从坐标(0, 0)到坐标(100, 100)的正方形

canvas.drawRect(0, 0, 100, 100, paint);

如果要绘制正方形的边框,则通过设置画笔的Style来实现:

var paint = new Paint();

//设置画笔为描边,则绘制出来的图形都是轮廓

paint.setStyle(Paint.STYLE.STROKE);

//设置画笔颜色为红色

paint.setColor(colors.RED);

//绘制一个从坐标(0, 0)到坐标(100, 100)的正方形

canvas.drawRect(0, 0, 100, 100, paint);

结合画笔canvas可以绘制基本图形、图片等。

canvas.drawARGB(a, r, g, b)

canvas.draw

Keys

按键模拟部分提供了一些模拟物理按键的全局函数,包括Home、音量键、照相键等,有的函数依赖于无障碍服务,有的函数依赖于root权限。

一般来说,以大写字母开头的函数都依赖于root权限。执行此类函数时,如果没有root权限,则函数执行后没有效果,并会在控制台输出一个警告。

back()

  • 返回 {boolean}

模拟按下返回键。返回是否执行成功。 此函数依赖于无障碍服务。

home()

  • 返回 {boolean}

模拟按下Home键。返回是否执行成功。 此函数依赖于无障碍服务。

powerDialog()

  • 返回 {boolean}

弹出电源键菜单。返回是否执行成功。 此函数依赖于无障碍服务。

notifications()

  • 返回 {boolean}

拉出通知栏。返回是否执行成功。 此函数依赖于无障碍服务。

quickSettings()

  • 返回 {boolean}

显示快速设置(下拉通知栏到底)。返回是否执行成功。 此函数依赖于无障碍服务。

recents()

  • 返回 {boolean}

显示最近任务。返回是否执行成功。 此函数依赖于无障碍服务。

splitScreen()

  • 返回 {boolean}

分屏。返回是否执行成功。 此函数依赖于无障碍服务, 并且需要系统自身功能的支持。

Home()

模拟按下Home键。 此函数依赖于root权限。

Back()

模拟按下返回键。 此函数依赖于root权限。

Power()

模拟按下电源键。 此函数依赖于root权限。

Menu()

模拟按下菜单键。 此函数依赖于root权限。

VolumeUp()

按下音量上键。 此函数依赖于root权限。

VolumeDown()

按键音量上键。 此函数依赖于root权限。

Camera()

模拟按下照相键。

Up()

模拟按下物理按键上。 此函数依赖于root权限。

Down()

模拟按下物理按键下。 此函数依赖于root权限。

Left()

模拟按下物理按键左。 此函数依赖于root权限。

Right()

模拟按下物理按键右。 此函数依赖于root权限。

OK()

模拟按下物理按键确定。 此函数依赖于root权限。

Text(text)

  • text {string} 要输入的文字,只能为英文或英文符号 输入文字text。例如Text("aaa");

KeyCode(code)

  • code {number} | 要按下的按键的数字代码或名称。参见下表。 模拟物理按键。例如KeyCode(29)和KeyCode("KEYCODE_A")是按下A键。

附录: KeyCode对照表

KeyCode KeyEvent Value

  • KEYCODE_MENU 1
  • KEYCODE_SOFT_RIGHT 2
  • KEYCODE_HOME 3
  • KEYCODE_BACK 4
  • KEYCODE_CALL 5
  • KEYCODE_ENDCALL 6
  • KEYCODE_0 7
  • KEYCODE_1 8
  • KEYCODE_2 9
  • KEYCODE_3 10
  • KEYCODE_4 11
  • KEYCODE_5 12
  • KEYCODE_6 13
  • KEYCODE_7 14
  • KEYCODE_8 15
  • KEYCODE_9 16
  • KEYCODE_STAR 17
  • KEYCODE_POUND 18
  • KEYCODE_DPAD_UP 19
  • KEYCODE_DPAD_DOWN 20
  • KEYCODE_DPAD_LEFT 21
  • KEYCODE_DPAD_RIGHT 22
  • KEYCODE_DPAD_CENTER 23
  • KEYCODE_VOLUME_UP 24
  • KEYCODE_VOLUME_DOWN 25
  • KEYCODE_POWER 26
  • KEYCODE_CAMERA 27
  • KEYCODE_CLEAR 28
  • KEYCODE_A 29
  • KEYCODE_B 30
  • KEYCODE_C 31
  • KEYCODE_D 32
  • KEYCODE_E 33
  • KEYCODE_F 34
  • KEYCODE_G 35
  • KEYCODE_H 36
  • KEYCODE_I 37
  • KEYCODE_J 38
  • KEYCODE_K 39
  • KEYCODE_L 40
  • KEYCODE_M 41
  • KEYCODE_N 42
  • KEYCODE_O 43
  • KEYCODE_P 44
  • KEYCODE_Q 45
  • KEYCODE_R 46
  • KEYCODE_S 47
  • KEYCODE_T 48
  • KEYCODE_U 49
  • KEYCODE_V 50
  • KEYCODE_W 51
  • KEYCODE_X 52
  • KEYCODE_Y 53
  • KEYCODE_Z 54
  • KEYCODE_COMMA 55
  • KEYCODE_PERIOD 56
  • KEYCODE_ALT_LEFT 57
  • KEYCODE_ALT_RIGHT 58
  • KEYCODE_SHIFT_LEFT 59
  • KEYCODE_SHIFT_RIGHT 60
  • KEYCODE_TAB 61
  • KEYCODE_SPACE 62
  • KEYCODE_SYM 63
  • KEYCODE_EXPLORER 64
  • KEYCODE_ENVELOPE 65
  • KEYCODE_ENTER 66
  • KEYCODE_DEL 67
  • KEYCODE_GRAVE 68
  • KEYCODE_MINUS 69
  • KEYCODE_EQUALS 70
  • KEYCODE_LEFT_BRACKET 71
  • KEYCODE_RIGHT_BRACKET 72
  • KEYCODE_BACKSLASH 73
  • KEYCODE_SEMICOLON 74
  • KEYCODE_APOSTROPHE 75
  • KEYCODE_SLASH 76
  • KEYCODE_AT 77
  • KEYCODE_NUM 78
  • KEYCODE_HEADSETHOOK 79
  • KEYCODE_FOCUS 80
  • KEYCODE_PLUS 81
  • KEYCODE_MENU 82
  • KEYCODE_NOTIFICATION 83
  • KEYCODE_SEARCH 84
  • TAGLAST KEYCODE 85

Media

Stability: 2 - Stable

media模块提供多媒体编程的支持。目前仅支持音乐播放和媒体文件扫描。后续会结合UI加入视频播放等功能。

需要注意是,使用该模块播放音乐时是在后台异步播放的,在脚本结束后会自动结束播放,因此可能需要插入诸如sleep()的语句来使脚本保持运行。例如:

//播放音乐

media.playMusic("/sdcard/1.mp3");

//让音乐播放完

sleep(media.getMusicDuration());

media.scanFile(path)

  • path {string} 媒体文件路径

扫描路径path的媒体文件,将它加入媒体库中;或者如果该文件以及被删除,则通知媒体库移除该文件。

媒体库包括相册、音乐库等,因此该函数可以用于把某个图片文件加入相册。

//请求截图

requestScreenCapture(false);

//截图

var im = captureScreen();

var path = "/sdcard/screenshot.png";

//保存图片

im.saveTo(path);

//把图片加入相册

media.scanFile(path);

media.playMusic(path[, volume, looping])

  • path {string} 音乐文件路径
  • volume {number} 播放音量,为0~1的浮点数,默认为1
  • looping {boolean} 是否循环播放,如果looping为true则循环播放,默认为false

播放音乐文件path。该函数不会显示任何音乐播放界面。如果文件不存在或者文件不是受支持的音乐格式,则抛出UncheckedIOException异常。

//播放音乐

media.playMusic("/sdcard/1.mp3");

//让音乐播放完

sleep(media.getMusicDuration());

如果要循环播放音乐,则使用looping参数:

//传递第三个参数为true以循环播放音乐 media.playMusic("/sdcard/1.mp3", 1, true); //等待三次播放的时间 sleep(media.getMusicDuration() * 3);

如果要使用音乐播放器播放音乐,调用app.viewFile(path)函数。

media.musicSeekTo(msec)

  • msec {number} 毫秒数,表示音乐进度

把当前播放进度调整到时间msec的位置。如果当前没有在播放音乐,则调用函数没有任何效果。

例如,要把音乐调到1分钟的位置,为media.musicSeekTo(60 * 1000)。

//播放音乐

media.playMusic("/sdcard/1.mp3");

//调整到30秒的位置

media.musicSeekTo(30 * 1000);

//等待音乐播放完成

sleep(media.getMusicDuration() - 30 * 1000);

media.pauseMusic()

暂停音乐播放。如果当前没有在播放音乐,则调用函数没有任何效果。

media.resumeMusic()

继续音乐播放。如果当前没有播放过音乐,则调用该函数没有任何效果。

media.stopMusic()

停止音乐播放。如果当前没有在播放音乐,则调用函数没有任何效果。

media.isMusicPlaying()

  • 返回 {boolean}

返回当前是否正在播放音乐。

media.getMusicDuration()

  • 返回 {number}

返回当前音乐的时长。单位毫秒。

media.getMusicCurrentPosition()

  • 返回 {number}

返回当前音乐的播放进度(已经播放的时间),单位毫秒。

module (模块)

Stability: 2 - Stable

Auto.js 有一个简单的模块加载系统。 在 Auto.js 中,文件和模块是一一对应的(每个文件被视为一个独立的模块)。

例子,假设有一个名为 foo.js 的文件:

var circle = require('circle.js');

console.log("半径为 4 的圆的面积是 %d", circle.area(4));

在第一行中,foo.js 加载了同一目录下的 circle.js 模块。

circle.js 文件的内容为:

const PI = Math.PI;

 

var circle = {};

 

circle.area = function (r) {

  return PI * r * r;

};

 

circle.circumference = (r) => 2 * PI * r;

 

module.exports = circle;

circle.js 模块导出了 area() 和 circumference() 两个函数。 通过在特殊的 exports 对象上指定额外的属性,函数和对象可以被添加到模块的根部。

模块内的本地变量是私有的。 在这个例子中,变量 PI 是 circle.js 私有的,不会影响到加载他的脚本的变量环境。

module.exports属性可以被赋予一个新的值(例如函数或对象)。

如下,bar.js 会用到 square 模块,square 导出一个构造函数:

const square = require('square.js');

const mySquare = square(2);

console.log("正方形的面积是 %d", mySquare.area());

square 模块定义在 square.js 中:

 

// 赋值给 `exports` 不会修改模块,必须使用 `module.exports`

module.exports = function(width) {

  return {

    area: () => width ** 2

  };

};

基于控件的操作

基于控件的操作指的是选择屏幕上的控件,获取其信息或对其进行操作。对于一般软件而言,基于控件的操作对不同机型有很好的兼容性;但是对于游戏而言,由于游戏界面并不是由控件构成,无法采用本章节的方法,也无法使用本章节的函数。有关游戏脚本的编写,请参考《基于坐标的操作》。

基于控件的操作依赖于无障碍服务,因此最好在脚本开头使用auto()函数来确保无障碍服务已经启用。如果运行到某个需要权限的语句无障碍服务并没启动,则会抛出异常并跳转到无障碍服务界面。这样的用户体验并不好,因为需要重新运行脚本,后续会加入等待无障碍服务启动并让脚本继续运行的函数。

您也可以在脚本开头使用"auto";表示这个脚本需要无障碍服务,但是不推荐这种做法,因为这个标记必须在脚本的最开头(前面不能有注释或其他语句、空格等),我们推荐使用auto()函数来确保无障碍服务已启用。

auto([mode])

  • mode {string} 模式

检查无障碍服务是否已经启用,如果没有启用则抛出异常并跳转到无障碍服务启用界面;同时设置无障碍模式为mode。mode的可选值为:

  • fast 快速模式。该模式下会启用控件缓存,从而选择器获取屏幕控件更快。对于需要快速的控件操作的脚本可以使用该模式,一般脚本则没有必要使用该函数。
  • normal 正常模式,默认。

如果不加mode参数,则为正常模式。

建议使用auto.waitFor()和auto.setMode()代替该函数,因为auto()函数如果无障碍服务未启动会停止脚本;而auto.waitFor()则会在在无障碍服务启动后继续运行。

示例:

auto("fast");

示例2:

auto();

auto.waitFor()

检查无障碍服务是否已经启用,如果没有启用则跳转到无障碍服务启用界面,并等待无障碍服务启动;当无障碍服务启动后脚本会继续运行。

auto.setMode(mode)

  • mode {string} 模式

设置无障碍模式为mode。mode的可选值为:

  • fast 快速模式。该模式下会启用控件缓存,从而选择器获取屏幕控件更快。对于需要快速的控件查看和操作的脚本可以使用该模式,一般脚本则没有必要使用该函数。
  • normal 正常模式,默认。

SimpleActionAutomator

Stability: 2 - Stable

SimpleActionAutomator提供了一些模拟简单操作的函数,例如点击文字、模拟按键等。这些函数可以直接作为全局函数使用。

click(text[, i])

  • text {string} 要点击的文本
  • i {number} 如果相同的文本在屏幕中出现多次,则i表示要点击第几个文本, i从0开始计算

返回是否点击成功。当屏幕中并未包含该文本,或者该文本所在区域不能点击时返回false,否则返回true。

该函数可以点击大部分包含文字的按钮。例如微信主界面下方的"微信", "联系人", "发现", "我"的按钮。
通常与while同时使用以便点击按钮直至成功。例如:

while(!click("扫一扫"));

当不指定参数i时则会尝试点击屏幕上出现的所有文字text并返回是否全部点击成功。

i是从0开始计算的, 也就是, click("啦啦啦", 0)表示点击屏幕上第一个"啦啦啦", click("啦啦啦", 1)表示点击屏幕上第二个"啦啦啦"。

文本所在区域指的是,从文本处向其父视图寻找,直至发现一个可点击的部件为止。

click(left, top, bottom, right)

  • left {number} 要点击的长方形区域左边与屏幕左边的像素距离
  • top {number} 要点击的长方形区域上边与屏幕上边的像素距离
  • bottom {number} 要点击的长方形区域下边与屏幕下边的像素距离
  • right {number} 要点击的长方形区域右边与屏幕右边的像素距离

注意,该函数一般只用于录制的脚本中使用,在自己写的代码中使用该函数一般不要使用该函数。

点击在指定区域的控件。当屏幕中并未包含与该区域严格匹配的区域,或者该区域不能点击时返回false,否则返回true。

有些按钮或者部件是图标而不是文字(例如发送朋友圈的照相机图标以及QQ下方的消息、联系人、动态图标),这时不能通过click(text, i)来点击,可以通过描述图标所在的区域来点击。left, bottom, top, right描述的就是点击的区域。

至于要定位点击的区域,可以在悬浮窗使用布局分析工具查看控件的bounds属性。

通过无障碍服务录制脚本会生成该语句。

longClick(text[, i]))

  • text {string} 要长按的文本
  • i {number} 如果相同的文本在屏幕中出现多次,则i表示要长按第几个文本, i从0开始计算

返回是否点击成功。当屏幕中并未包含该文本,或者该文本所在区域不能点击时返回false,否则返回true。

当不指定参数i时则会尝试点击屏幕上出现的所有文字text并返回是否全部长按成功。

scrollUp([i])

  • i {number} 要滑动的控件序号

找到第i+1个可滑动控件上滑或左滑。返回是否操作成功。屏幕上没有可滑动的控件时返回false。

另外不加参数时scrollUp()会寻找面积最大的可滑动的控件上滑或左滑,例如微信消息列表等。

参数为一个整数i时会找到第i + 1个可滑动控件滑动。例如scrollUp(0)为滑动第一个可滑动控件。

scrollDown([i])

  • i {number} 要滑动的控件序号

找到第i+1个可滑动控件下滑或右滑。返回是否操作成功。屏幕上没有可滑动的控件时返回false。

另外不加参数时scrollUp()会寻找面积最大的可滑动的控件下滑或右滑。

参数为一个整数i时会找到第i + 1个可滑动控件滑动。例如scrollUp(0)为滑动第一个可滑动控件。

setText([i, ]text)

  • i {number} 表示要输入的为第i + 1个输入框
  • text {string} 要输入的文本

返回是否输入成功。当找不到对应的文本框时返回false。

不加参数i则会把所有输入框的文本都置为text。例如setText("测试")。

这里的输入文本的意思是,把输入框的文本置为text,而不是在原来的文本上追加。

input([i, ]text)

  • i {number} 表示要输入的为第i + 1个输入框
  • text {string} 要输入的文本

返回是否输入成功。当找不到对应的文本框时返回false。

不加参数i则会把所有输入框的文本追加内容text。例如input("测试")。

UiSelector

UiSelector即选择器,用于通过各种条件选取屏幕上的控件,再对这些控件进行点击、长按等动作。这里需要先简单介绍一下控件和界面的相关知识。

一般软件的界面是由一个个控件构成的,例如图片部分是一个图片控件(ImageView),文字部分是一个文字控件(TextView);同时,通过各种布局来决定各个控件的位置,例如,线性布局(LinearLayout)里面的控件都是按水平或垂直一次叠放的,列表布局(AbsListView)则是以列表的形式显示控件。

控件有各种属性,包括文本(text), 描述(desc), 类名(className), id等等。我们通常用一个控件的属性来找到这个控件,例如,想要点击QQ聊天窗口的"发送"按钮,我们就可以通过他的文本属性为"发送"来找到这个控件并点击他,具体代码为:

var sendButton = text("发送").findOne();

sendButton.click();

在这个例子中, text("发送")表示一个条件(文本属性为"发送"),findOne()表示基于这个条件找到一个符合条件的控件,从而我们可以得到发送按钮sendButton,再执行sendButton.click()即可点击"发送"按钮。

用文本属性来定位按钮控件、文本控件通常十分有效。但是,如果一个控件是图片控件,比如Auto.js主界面右上角的搜索图标,他没有文本属性,这时需要其他属性来定位他。我们如何查看他有什么属性呢?首先打开悬浮窗和无障碍服务,点击蓝色的图标(布局分析), 可以看到以下界面:

之后我们点击搜索图标,可以看到他有以下属性:

我们注意到这个图标的desc(描述)属性为"搜索",那么我们就可以通过desc属性来定位这个控件,得到点击搜索图标的代码为:

desc("搜索").findOne().click();

可能心细的你可能注意到了,这个控件还有很多其他的属性,例如checked, className, clickable等等,为什么不用这些属性来定位搜索图标呢?答案是,其他控件也有这些值相同的属性、尝试一下你就可以发现很多其他控件的checked属性和搜索控件一样都是false,如果我们用checked(false)作为条件,将会找到很多控件,而无法确定哪一个是搜索图标。因此,要找到我们想要的那个控件,选择器的条件通常需要是可唯一确定控件的。我们通常用一个独一无二的属性来定位一个控件,例如这个例子中就没有其他控件的desc(描述)属性为"搜索"。

另外,对于这个搜索图标而言,id属性也是唯一的,我们也可以用id("action_search").findOne().click()来点击这个控件。如果一个控件有id属性,那么这个属性很可能是唯一的,除了以下几种情况:

  • QQ的控件的id属性很多都是"name",也就是在QQ界面难以通过id来定位一个控件
  • 列表中的控件,比如QQ联系人列表,微信联系人列表等

尽管id属性很方便,但也不总是最方便的,例如对于微信和网易云音乐,每次更新他的控件id都会变化,导致了相同代码对于不同版本的微信、网易云音乐并不兼容。

除了这些属性外,主要还有以下几种属性:

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

有时候只靠一个属性并不能唯一确定一个控件,这时需要通过属性的组合来完成定位,例如className("ImageView").depth(10).findOne().click(),通过链式调用来组合条件。

通常用这些技巧便可以解决大部分问题,即使解决不了问题,也可以通过布局分析的"生成代码"功能来尝试生成一些选择器代码。接下来的问题便是对选取的控件进行操作,包括:

  • click() 点击。点击一个控件,前提是这个控件的clickable属性为true
  • longClick() 长按。长按一个控件,前提是这个控件的longClickable属性为true
  • setText() 设置文本,用于编辑框控件设置文本。
  • scrollForward(), scrollBackward() 滑动。滑动一个控件(列表等), 前提是这个控件的scrollable属性为true
  • exits() 判断控件是否存在
  • waitFor() 等待控件出现

这些操作包含了绝大部分控件操作。根据这些我们可以很容易写出一个"刷屏"脚本(代码仅为示例,请不要在别人的群里测试,否则容易被踢):

while(true){

    className("EditText").findOne().setText("刷屏...");

    text("发送").findOne().clicK();

}

上面这段代码也可以写成:

while(true){

    className("EditText").setText("刷屏...");

    text("发送").clicK();

}

如果不加findOne()而直接进行操作,则选择器会找出所有符合条件的控件并操作。

另外一个比较常用的操作的滑动。滑动操作的第一步是找到需要滑动的控件,例如要滑动QQ消息列表则在悬浮窗布局层次分析中找到AbsListView,这个控件就是消息列表控件,如下图:

长按可查看控件信息,注意到其scrollable属性为true,并找出其id为"recent_chat_list",从而下滑QQ消息列表的代码为:

id("recent_chat_list").className("AbsListView").findOne().scrollForward();

scrollForward()为向前滑,包括下滑和右滑。

选择器的入门教程暂且要这里,更多信息可以查看下面的文档和选择器进阶。

selector()

  • 返回 {UiSelector}

创建一个新的选择器。但一般情况不需要使用该函数,因为可以直接用相应条件的语句创建选择器。

由于历史遗留原因,本不应该这样设计(不应该让id(), text()等作为全局函数,而是应该用By.id(), By.text()),但为了后向兼容性只能保留这个设计。

这样的API设计会污染全局变量,后续可能会支持"去掉这些全局函数而使用By.*"的选项。

UiSelector.text(str)

  • str {string} 控件文本
  • 返回 {UiSelector} 返回选择器自身以便链式调用

为当前选择器附加控件"text等于字符串str"的筛选条件。

控件的text(文本)属性是文本控件上的显示的文字,例如微信左上角的"微信"文本。

UiSelector.textContains(str)

  • str {string} 要包含的字符串

为当前选择器附加控件"text需要包含字符串str"的筛选条件。

这是一个比较有用的条件,例如QQ动态页和微博发现页上方的"大家都在搜...."的控件可以用textContains("大家都在搜").findOne()来获取。

UiSelector.textStartsWith(prefix)

  • prefix {string} 前缀

为当前选择器附加控件"text需要以prefix开头"的筛选条件。

这也是一个比较有用的条件,例如要找出Auto.js脚本列表中名称以"QQ"开头的脚本的代码为textStartsWith("QQ").find()。

UiSelector.textEndsWith(suffix)

  • suffix {string} 后缀

为当前选择器附加控件"text需要以suffix结束"的筛选条件。

UiSelector.textMatches(reg)

  • reg {string} | {Regex} 要满足的正则表达式。

为当前选择器附加控件"text需要满足正则表达式reg"的条件。

有关正则表达式,可以查看正则表达式 - 菜鸟教程

需要注意的是,如果正则表达式是字符串,则需要使用\\来表达\(也即Java正则表达式的形式),例如textMatches("\\d+")匹配多位数字;但如果使用JavaScript语法的正则表达式则不需要,例如textMatches(/\d+/)。但如果使用字符串的正则表达式则该字符串不能以"/"同时以"/"结束,也即不能写诸如textMatches("/\\d+/")的表达式,否则会被开头的"/"和结尾的"/"会被忽略。

UiSelector.desc(str)

  • str {string} 控件文本
  • 返回 {UiSelector} 返回选择器自身以便链式调用

为当前选择器附加控件"desc等于字符串str"的筛选条件。

控件的desc(描述,全称为Content-Description)属性是对一个控件的描述,例如网易云音乐右上角的放大镜图标的描述为搜索。要查看一个控件的描述,同样地可以借助悬浮窗查看。

desc属性同样是定位控件的利器。

UiSelector.descContains(str)

  • str {string} 要包含的字符串

为当前选择器附加控件"desc需要包含字符串str"的筛选条件。

UiSelector.descStartsWith(prefix)

  • prefix {string} 前缀

为当前选择器附加控件"desc需要以prefix开头"的筛选条件。

UiSelector.descEndsWith(suffix)

  • suffix {string} 后缀

为当前选择器附加控件"desc需要以suffix结束"的筛选条件。

UiSelector.descMatches(reg)

  • reg {string} | {Regex} 要满足的正则表达式。

为当前选择器附加控件"desc需要满足正则表达式reg"的条件。

有关正则表达式,可以查看正则表达式 - 菜鸟教程

需要注意的是,如果正则表达式是字符串,则需要使用\\来表达\(也即Java正则表达式的形式),例如textMatches("\\d+")匹配多位数字;但如果使用JavaScript语法的正则表达式则不需要,例如textMatches(/\d+/)。但如果使用字符串的正则表达式则该字符串不能以"/"同时以"/"结束,也即不能写诸如textMatches("/\\d+/")的表达式,否则会被开头的"/"和结尾的"/"会被忽略。

UiSelector.id(resId)

  • resId {string} 控件的id,以"包名:id/"开头,例如"com.tencent.mm:id/send_btn"。也可以不指定包名,这时会以当前正在运行的应用的包名来补全id。例如id("send_btn"),在QQ界面想当于id("com.tencent.mobileqq:id/send_btn")。

为当前选择器附加"id等于resId"的筛选条件。

控件的id属性通常是可以用来确定控件的唯一标识,如果一个控件有id,那么使用id来找到他是最好的方法。要查看屏幕上的控件的id,可以开启悬浮窗并使用界面工具,点击相应控件即可查看。若查看到的控件id为null, 表示该控件没有id。另外,在列表中会出现多个控件的id相同的情况。例如微信的联系人列表,每个头像的id都是一样的。此时不能用id来唯一确定控件。

在QQ界面经常会出现多个id为"name"的控件,在微信上则每个版本的id都会变化。对于这些软件而言比较难用id定位控件。

UiSelector.idContains(str)

  • str {string} id要包含的字符串

为当前选择器附加控件"id包含字符串str"的筛选条件。比较少用。

UiSelector.idStartsWith(prefix)

  • prefix {string} id前缀

为当前选择器附加"id需要以prefix开头"的筛选条件。比较少用。

UiSelector.idEndsWith(suffix)

  • suffix {string} id后缀

为当前选择器附加"id需要以suffix结束"的筛选条件。比较少用。

UiSelector.idMatches(reg)

  • reg {Regex} | {string} id要满足的正则表达式

附加id需要满足正则表达式。

需要注意的是,如果正则表达式是字符串,则需要使用\\来表达\(也即Java正则表达式的形式),例如textMatches("\\d+")匹配多位数字;但如果使用JavaScript语法的正则表达式则不需要,例如textMatches(/\d+/)。但如果使用字符串的正则表达式则该字符串不能以"/"同时以"/"结束,也即不能写诸如textMatches("/\\d+/")的表达式,否则会被开头的"/"和结尾的"/"会被忽略。

idMatches("[a-zA-Z]+")

UiSelector.className(str)

  • str {string} 控件文本
  • 返回 {UiSelector} 返回选择器自身以便链式调用

为当前选择器附加控件"className等于字符串str"的筛选条件。

控件的className(类名)表示一个控件的类别,例如文本控件的类名为android.widget.TextView。

如果一个控件的类名以"android.widget."开头,则可以省略这部分,例如文本控件可以直接用className("TextView")的选择器。

常见控件的类名如下:

  • 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 通常也是列表控件

UiSelector.classNameContains(str)

  • str {string} 要包含的字符串

为当前选择器附加控件"className需要包含字符串str"的筛选条件。

UiSelector.classNameStartsWith(prefix)

  • prefix {string} 前缀

为当前选择器附加控件"className需要以prefix开头"的筛选条件。

UiSelector.classNameEndsWith(suffix)

  • suffix {string} 后缀

为当前选择器附加控件"className需要以suffix结束"的筛选条件。

UiSelector.classNameMatches(reg)

  • reg {string} | {Regex} 要满足的正则表达式。

为当前选择器附加控件"className需要满足正则表达式reg"的条件。

有关正则表达式,可以查看正则表达式 - 菜鸟教程

需要注意的是,如果正则表达式是字符串,则需要使用\\来表达\(也即Java正则表达式的形式),例如textMatches("\\d+")匹配多位数字;但如果使用JavaScript语法的正则表达式则不需要,例如textMatches(/\d+/)。但如果使用字符串的正则表达式则该字符串不能以"/"同时以"/"结束,也即不能写诸如textMatches("/\\d+/")的表达式,否则会被开头的"/"和结尾的"/"会被忽略。

UiSelector.packageName(str)

  • str {string} 控件文本
  • 返回 {UiSelector} 返回选择器自身以便链式调用

为当前选择器附加控件"packageName等于字符串str"的筛选条件。

控件的packageName表示控件所属界面的应用包名。例如微信的包名为"com.tencent.mm", 那么微信界面的控件的packageName为"com.tencent.mm"。

要查看一个应用的包名,可以用函数app.getPackageName()获取,例如toast(app.getPackageName("微信"))。

UiSelector.packageNameContains(str)

  • str {string} 要包含的字符串

为当前选择器附加控件"packageName需要包含字符串str"的筛选条件。

UiSelector.packageNameStartsWith(prefix)

  • prefix {string} 前缀

为当前选择器附加控件"packageName需要以prefix开头"的筛选条件。

UiSelector.packageNameEndsWith(suffix)

  • suffix {string} 后缀

为当前选择器附加控件"packageName需要以suffix结束"的筛选条件。

UiSelector.packageNameMatches(reg)

  • reg {string} | {Regex} 要满足的正则表达式。

为当前选择器附加控件"packageName需要满足正则表达式reg"的条件。

有关正则表达式,可以查看正则表达式 - 菜鸟教程

UiSelector.bounds(left, top, right, buttom)

  • left {number} 控件左边缘与屏幕左边的距离
  • top {number} 控件上边缘与屏幕上边的距离
  • right {number} 控件右边缘与屏幕左边的距离
  • bottom {number} 控件下边缘与屏幕上边的距离

一个控件的bounds属性为这个控件在屏幕上显示的范围。我们可以用这个范围来定位这个控件。尽管用这个方法定位控件对于静态页面十分准确,却无法兼容不同分辨率的设备;同时对于列表页面等动态页面无法达到效果,因此使用不推荐该选择器。

注意参数的这四个数字不能随意填写,必须精确的填写控件的四个边界才能找到该控件。例如,要点击QQ主界面的右上角加号,我们用布局分析查看该控件的属性,如下图:

可以看到bounds属性为(951, 67, 1080, 196),此时使用代码bounds(951, 67, 1080, 196).clickable().click()即可点击该控件。

UiSelector.boundsInside(left, top, right, buttom)

  • left {number} 范围左边缘与屏幕左边的距离
  • top {number} 范围上边缘与屏幕上边的距离
  • right {number} 范围右边缘与屏幕左边的距离
  • bottom {number} 范围下边缘与屏幕上边的距离

为当前选择器附加控件"bounds需要在left, top, right, buttom构成的范围里面"的条件。

这个条件用于限制选择器在某一个区域选择控件。例如要在屏幕上半部分寻找文本控件TextView,代码为:

var w = className("TextView").boundsInside(0, 0, device.width, device.height / 2).findOne();

log(w.text());

其中我们使用了device.width来获取屏幕宽度,device.height来获取屏幕高度。

UiSelector.boundsContains(left, top, right, buttom)

  • left {number} 范围左边缘与屏幕左边的距离
  • top {number} 范围上边缘与屏幕上边的距离
  • right {number} 范围右边缘与屏幕左边的距离
  • bottom {number} 范围下边缘与屏幕上边的距离

为当前选择器附加控件"bounds需要包含left, top, right, buttom构成的范围"的条件。

这个条件用于限制控件的范围必须包含所给定的范围。例如给定一个点(500, 300), 寻找在这个点上的可点击控件的代码为:

var w = boundsContains(500, 300, device.width - 500, device.height - 300).clickable().findOne();

w.click();

UiSelector.drawingOrder(order)

  • order {number} 控件在父视图中的绘制顺序

为当前选择器附加控件"drawingOrder等于order"的条件。

drawingOrder为一个控件在父控件中的绘制顺序,通常可以用于区分同一层次的控件。

但该属性在Android 7.0以上才能使用。

UiSelector.clickable([b = true])

  • b {Boolean} 表示控件是否可点击

为当前选择器附加控件是否可点击的条件。但并非所有clickable为false的控件都真的不能点击,这取决于控件的实现。对于自定义控件(例如显示类名为android.view.View的控件)很多的clickable属性都为false都却能点击。

需要注意的是,可以省略参数b而表示选择那些可以点击的控件,例如className("ImageView").clickable()表示可以点击的图片控件的条件,className("ImageView").clickable(false)表示不可点击的图片控件的条件。

UiSelector.longClickable([b = true])

  • b {Boolean} 表示控件是否可长按

为当前选择器附加控件是否可长按的条件。

UiSelector.checkable([b = true])

  • b {Boolean} 表示控件是否可勾选

为当前选择器附加控件是否可勾选的条件。勾选通常是对于勾选框而言的,例如图片多选时左上角通常有一个勾选框。

UiSelector.selected([b = true])

  • b {Boolean} 表示控件是否被选

为当前选择器附加控件是否已选中的条件。被选中指的是,例如QQ聊天界面点击下方的"表情按钮"时,会出现自己收藏的表情,这时"表情按钮"便处于选中状态,其selected属性为true。

UiSelector.enabled([b = true])

  • b {Boolean} 表示控件是否已启用

为当前选择器附加控件是否已启用的条件。大多数控件都是启用的状态(enabled为true),处于“禁用”状态通常是灰色并且不可点击。

UiSelector.scrollable([b = true])

  • b {Boolean} 表示控件是否可滑动

为当前选择器附加控件是否可滑动的条件。滑动包括上下滑动和左右滑动。

可以用这个条件来寻找可滑动控件来滑动界面。例如滑动Auto.js的脚本列表的代码为:

className("android.support.v7.widget.RecyclerView").scrollable().findOne().scrollForward();

//或者classNameEndsWith("RecyclerView").scrollable().findOne().scrollForward();

UiSelector.editable([b = true])

  • b {Boolean} 表示控件是否可编辑

为当前选择器附加控件是否可编辑的条件。一般来说可编辑的控件为输入框(EditText),但不是所有的输入框(EditText)都可编辑。

UiSelector.multiLine([b = true])

  • b {Boolean} 表示文本或输入框控件是否是多行显示的

为当前选择器附加控件是否文本或输入框控件是否是多行显示的条件。

UiSelector.findOne()

  • 返回 UiObject

根据当前的选择器所确定的筛选条件,对屏幕上的控件进行搜索,直到屏幕上出现满足条件的一个控件为止,并返回该控件。如果找不到控件,当屏幕内容发生变化时会重新寻找,直至找到。

需要注意的是,如果屏幕上一直没有出现所描述的控件,则该函数会阻塞,直至所描述的控件出现为止。因此此函数不会返回null。

该函数本来应该命名为untilFindOne(),但由于历史遗留原因已经无法修改。如果想要只在屏幕上搜索一次而不是一直搜索,请使用findOnce()。

另外,如果屏幕上有多个满足条件的控件,findOne()采用深度优先搜索(DFS),会返回该搜索算法找到的第一个控件。注意控件找到的顺序有时会起到作用。

UiSelector.findOne(timeout)

  • timeout {number} 搜索的超时时间,单位毫秒
  • 返回 UiObject

根据当前的选择器所确定的筛选条件,对屏幕上的控件进行搜索,直到屏幕上出现满足条件的一个控件为止,并返回该控件;如果在timeout毫秒的时间内没有找到符合条件的控件,则终止搜索并返回null。

该函数类似于不加参数的findOne(),只不过加上了时间限制。

示例:

//启动Auto.js

launchApp("Auto.js");

//在6秒内找出日志图标的控件

var w = id("action_log").findOne(6000);

//如果找到控件则点击

if(w != null){

    w.click();

}else{

    //否则提示没有找到

    toast("没有找到日志图标");

}

UiSelector.findOnce()

  • 返回 UiObject

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

UiSelector.findOnce(i)

  • i {number} 索引

根据当前的选择器所确定的筛选条件,对屏幕上的控件进行搜索,并返回第 i + 1 个符合条件的控件;如果没有找到符合条件的控件,或者符合条件的控件个数 < i, 则返回null。

注意这里的控件次序,是搜索算法深度优先搜索(DSF)决定的。

UiSelector.find()

  • 返回 UiCollection

根据当前的选择器所确定的筛选条件,对屏幕上的控件进行搜索,找到所有满足条件的控件集合并返回。这个搜索只进行一次,并不保证一定会找到,因而会出现返回的控件集合为空的情况。

不同于findOne()或者findOnce()只找到一个控件并返回一个控件,find()函数会找出所有满足条件的控件并返回一个控件集合。之后可以对控件集合进行操作。

可以通过empty()函数判断找到的是否为空。例如:

var c = className("AbsListView").find();

if(c.empty()){

    toast("找到啦");

}else{

    toast("没找到╭(╯^╰)╮");

}

UiSelector.untilFind()

  • 返回 UiCollection

根据当前的选择器所确定的筛选条件,对屏幕上的控件进行搜索,直到找到至少一个满足条件的控件为止,并返回所有满足条件的控件集合。

该函数与find()函数的区别在于,该函数永远不会返回空集合;但是,如果屏幕上一直没有出现满足条件的控件,则该函数会保持阻塞。

UiSelector.exists()

  • 返回 {Boolean}

判断屏幕上是否存在控件符合选择器所确定的条件。例如要判断某个文本出现就执行某个动作,可以用:

if(text("某个文本").exists()){

    //要支持的动作

}

UiSelector.waitFor()

等待屏幕上出现符合条件的控件;在满足该条件的控件出现之前,该函数会一直保持阻塞。

例如要等待包含"哈哈哈"的文本控件出现的代码为:

textContains("哈哈哈").waitFor();

UiSelector.filter(f)

  • f {Function} 过滤函数,参数为UiObject,返回值为boolean

为当前选择器附加自定义的过滤条件。

例如,要找出屏幕上所有文本长度为10的文本控件的代码为:

var uc = className("TextView").filter(function(w){

    return w.text().length == 10;

});

UiObject

UiObject表示一个控件,可以通过这个对象获取到控件的属性,也可以对控件进行点击、长按等操作。

获取一个UiObject通常通过选择器的findOne(), findOnce()等函数,也可以通过UiCollection来获取,或者通过UiObject.child(), UiObject.parent()等函数来获取一个控件的子控件或父控件。

UiObject.click()

  • 返回 {Boolean}

点击该控件,并返回是否点击成功。

如果该函数返回false,可能是该控件不可点击(clickable为false),当前界面无法响应该点击等。

UiObject.longClick()

  • 返回 {Boolean}

长按该控件,并返回是否点击成功。

如果该函数返回false,可能是该控件不可点击(longClickable为false),当前界面无法响应该点击等。

UiObject.setText(text)

  • text {string} 文本
  • 返回 {Boolean}

设置输入框控件的文本内容,并返回是否设置成功。

该函数只对可编辑的输入框(editable为true)有效。

UiObject.copy()

  • 返回 {Boolean}

对输入框文本的选中内容进行复制,并返回是否操作成功。

该函数只能用于输入框控件,并且当前输入框控件有选中的文本。可以通过setSelection()函数来设置输入框选中的内容。

var et = className("EditText").findOne();

//选中前两个字

et.setSelection(0, 2);

//对选中内容进行复制

if(et.copy()){

    toast("复制成功");

}else{

    toast("复制失败");

}

UiObject.cut()

对输入框文本的选中内容进行剪切,并返回是否操作成功。

该函数只能用于输入框控件,并且当前输入框控件有选中的文本。可以通过setSelection()函数来设置输入框选中的内容。

UiObject.paste()

  • 返回 {Boolean}

对输入框控件进行粘贴操作,把剪贴板内容粘贴到输入框中,并返回是否操作成功。

//设置剪贴板内容为“你好”

setClip("你好");

var et = className("EditText").findOne();

et.paste();

UiObject.setSelection(start, end)

  • start {number} 选中内容起始位置
  • end {number} 选中内容结束位置(不包括)
  • 返回 {Boolean}

对输入框控件设置选中的文字内容,并返回是否操作成功。

索引是从0开始计算的;并且,选中内容不包含end位置的字符。例如,如果一个输入框内容为"123456789",要选中"4567"的文字的代码为et.setSelection(3, 7)。

该函数也可以用来设置光标位置,只要参数的end等于start,即可把输入框光标设置在start的位置。例如et.setSelection(1, 1)会把光标设置在第一个字符的后面。

UiObject.scrollForward()

  • 返回 {Boolean}

对控件执行向前滑动的操作,并返回是否操作成功。

向前滑动包括了向右和向下滑动。如果一个控件既可以向右滑动和向下滑动,那么执行scrollForward()的行为是未知的(这是因为Android文档没有指出这一点,同时也没有充分的测试可供参考)。

UiObject.scrollBackward()

  • 返回 {Boolean}

对控件执行向后滑动的操作,并返回是否操作成功。

向后滑动包括了向右和向下滑动。如果一个控件既可以向右滑动和向下滑动,那么执行scrollForward()的行为是未知的(这是因为Android文档没有指出这一点,同时也没有充分的测试可供参考)。

UiObject.select()

  • 返回 {Boolean}

对控件执行"选中"操作,并返回是否操作成功。"选中"和isSelected()的属性相关,但该操作十分少用。

UiObject.collapse()

  • 返回 {Boolean}

对控件执行折叠操作,并返回是否操作成功。

UiObject.expand()

  • 返回 {Boolean}

对控件执行操作,并返回是否操作成功。

UiObject.show()

对集合中所有控件执行显示操作,并返回是否全部操作成功。

UiObject.scrollUp()

对集合中所有控件执行向上滑的操作,并返回是否全部操作成功。

UiObject.scrollDown()

对集合中所有控件执行向下滑的操作,并返回是否全部操作成功。

UiObject.scrollLeft()

对集合中所有控件执行向左滑的操作,并返回是否全部操作成功。

UiObject.scrollRight()

children()

  • 返回 UiCollection

返回该控件的所有子控件组成的控件集合。可以用于遍历一个控件的子控件,例如:

className("AbsListView").findOne().children()

    .forEach(function(child){

        log(child.className());

    });

childCount()

  • 返回 {number}

返回子控件数目。

child(i)

  • i {number} 子控件索引
  • 返回 {UiObject}

返回第i+1个子控件。如果i>=控件数目或者小于0,则抛出异常。

需要注意的是,由于布局捕捉的问题,该函数可能返回null,也就是可能获取不到某个子控件。

遍历子控件的示例:

var list = className("AbsListView").findOne();

for(var i = 0; i < list.childCount(); i++){

    var child = list.child(i);

    log(child.className());

}

parent()

  • 返回 {UiObject}

返回该控件的父控件。如果该控件没有父控件,返回null。

bounds()

  • 返回 Rect

返回控件在屏幕上的范围,其值是一个Rect对象。

示例:

var b = text("Auto.js").findOne().bounds();

toast("控件在屏幕上的范围为" + b);

如果一个控件本身无法通过click()点击,那么我们可以利用bounds()函数获取其坐标,再利用坐标点击。例如:

var b = desc("打开侧拉菜单").findOne().bounds();

click(b.centerX(), b.centerY());

//如果使用root权限,则用 Tap(b.centerX(), b.centerY());

boundsInParent()

  • 返回 Rect

返回控件在父控件中的范围,其值是一个Rect对象。

drawingOrder()

  • 返回 {number}

返回控件在父控件中的绘制次序。该函数在安卓7.0及以上才有效,7.0以下版本调用会返回0。

id()

  • 返回 {string}

获取控件的id,如果一个控件没有id,则返回null。

text()

  • 返回 {string}

获取控件的文本,如果控件没有文本,返回""。

findByText(str)

  • str {string} 文本
  • 返回 UiCollection

根据文本text在子控件中递归地寻找并返回文本或描述(desc)包含这段文本str的控件,返回它们组成的集合。

该函数会在当前控件的子控件,孙控件,曾孙控件...中搜索text或desc包含str的控件,并返回它们组合的集合。

findOne(selector)

  • selector UiSelector
  • 返回 UiOobject

根据选择器selector在该控件的子控件、孙控件...中搜索符合该选择器条件的控件,并返回找到的第一个控件;如果没有找到符合条件的控件则返回null。

例如,对于酷安动态列表,我们可以遍历他的子控件(每个动态列表项),并在每个子控件中依次寻找点赞数量和图标,对于点赞数量小于10的点赞:

//找出动态列表

var list = id("recycler_view").findOne();

//遍历动态

list.children().forEach(function(child){

    //找出点赞图标

    var like = child.findOne(id("feed_action_view_like"));

    //找出点赞数量

    var likeCount = child.findOne(id("text_view"));

    //如果这两个控件没有找到就不继续了

    if(like == null || likeCount == null){

        return;

    }

    //判断点赞数量是否小于10

    if(parseInt(likeCount.text()) < 10){

        //点赞

        like.click();

    }

});

find(selector)

  • selector UiSelector
  • 返回 UiCollection

根据选择器selector在该控件的子控件、孙控件...中搜索符合该选择器条件的控件,并返回它们组合的集合。

UiCollection

UiCollection, 控件集合, 通过选择器的find(), untilFind()方法返回的对象。

UiCollection"继承"于数组,实际上是一个UiObject的数组,因此可以使用数组的函数和属性,例如使用length属性获取UiCollection的大小,使用forEach函数来遍历UiCollection。

例如,采用forEach遍历屏幕上所有的文本控件并打印出文本内容的代码为:

console.show();

className("TextView").find().forEach(function(tv){

    if(tv.text() != ""){

        log(tv.text());

    }

});

也可以使用传统的数组遍历方式:

console.show();

var uc = className("TextView").find();

for(var i = 0; i < uc.length; i++){

    var tv = uc[i];

    if(tv.text() != ""){

        log(tv.text());

    }

}

UiCollection的每一个元素都是UiObject,我们可以取出他的元素进行操作,例如取出第一个UiObject并点击的代码为ui[0].click()。如果想要对该集合的所有元素进行操作,可以直接在集合上调用相应的函数,例如uc.click(),该代码会对集合上所有UiObject执行点击操作并返回是否全部点击成功。

因此,UiCollection具有所有UiObject对控件操作的函数,包括click(), longClick(), scrollForward()等等,不再赘述。

UiCollection.size()

  • 返回 {number}

返回集合中的控件数。

历史遗留函数,相当于属性length。

UiCollection.get(i)

  • i {number} 索引
  • 返回 UiObject

返回集合中第i+1个控件(UiObject)。

历史遗留函数,建议直接使用数组下标的方式访问元素。

UiCollection.each(func)

  • func {Function} 遍历函数,参数为UiObject。

遍历集合。

历史遗留函数,相当于forEach。参考forEach

empty()

  • 返回 {Boolean}

返回控件集合是否为空。

nonEmpty()

  • 返回 {Boolean}

返回控件集合是否非空。

UiCollection.find(selector)

  • selector UiSelector
  • 返回 UiCollection

根据selector所确定的条件在该控件集合的控件、子控件、孙控件...中找到所有符合条件的控件并返回找到的控件集合。

注意这会递归地遍历控件集合里所有的控件以及他们的子控件。和数组的filter函数不同。

例如:

var names = id("name").find();

//在集合

var clickableNames = names.find(clickable());

UiCollection.findOne(selector)

  • selector UiSelector
  • 返回 UiOobject

根据选择器selector在该控件集合的控件的子控件、孙控件...中搜索符合该选择器条件的控件,并返回找到的第一个控件;如果没有找到符合条件的控件则返回null。

Rect

UiObject.bounds(), UiObject.boundsInParent()返回的对象。表示一个长方形(范围)。

Rect.left

  • {number}

长方形左边界的x坐标、

Rect.right

  • {number}

长方形右边界的x坐标、

Rect.top

  • {number}

长方形上边界的y坐标、

Rect.bottom

  • {number}

长方形下边界的y坐标、

Rect.centerX()

  • 返回 {number}

长方形中点x坐标。

Rect.centerY()

  • 返回 {number}

长方形中点y坐标。

Rect.width()

  • 返回 {number}

长方形宽度。通常可以作为控件宽度。

Rect.height()

  • 返回 {number}

长方形高度。通常可以作为控件高度。

Rect.contains(r)

  • Rect

返回是否包含另一个长方形r。包含指的是,长方形r在该长方形的里面(包含边界重叠的情况)。

Rect.intersect(r)

  • Rect

返回是否和另一个长方形相交。

UiSelector进阶

未完待续。

Sensors

Stability: 2 - Stable

sensors模块提供了获取手机上的传感器的信息的支持,这些传感器包括距离传感器、光线光感器、重力传感器、方向传感器等。需要指出的是,脚本只能获取传感器的数据,不能模拟或伪造传感器的数据和事件,因此诸如模拟摇一摇的功能是无法实现的。

要监听一个传感器时,需要使用sensors.register()注册监听器,之后才能开始监听;不需要监听时则调用sensors.unregister()注销监听器。在脚本结束时会自动注销所有的监听器。同时,这种监听会使脚本保持运行状态,如果不注销监听器,脚本会一直保持运行状态。

例如,监听光线传感器的代码为:

//光线传感器监听

sensors.register("light").on("change", (event, light)=>{

    log("当前光强度为", light);

});

要注意的是,每个传感器的数据并不相同,所以对他们调用on()监听事件时的回调函数参数也不是相同,例如光线传感器参数为(event, light),加速度传感器参数为(event, ax, ay, az)。甚至在某些设备上的传感器参数有所增加,例如华为手机的距离传感器为三个参数,一般手机只有一个参数。

常用的传感器及其事件参数如下表:

  • accelerometer 加速度传感器,参数(event, ax, ay, az):
    • event SensorEvent 传感器事件,用于获取传感器数据变化时的所有信息
    • ax {number} x轴上的加速度,单位m/s^2
    • ay {number} y轴上的加速度,单位m/s^2
    • az {number} z轴上的加速度,单位m/s^2 这里的x轴,y轴,z轴所属的坐标系统如下图(其中z轴垂直于设备屏幕表面):

!

  • orientation 方向传感器,参数(event, azimuth, pitch, roll):
    • event SensorEvent 传感器事件,用于获取传感器数据变化时的所有信息
    • azimuth {number} 方位角,从地磁指北方向线起,依顺时针方向到y轴之间的水平夹角,单位角度,范围0~359
    • pitch {number} 绕x轴旋转的角度,当设备水平放置时该值为0,当设备顶部翘起时该值为正数,当设备尾部翘起时该值为负数,单位角度,范围-180~180
    • roll {number} 绕y轴顺时针旋转的角度,单位角度,范围-90~90
  • gyroscope 陀螺仪传感器,参数(event, wx, wy, wz):
    • event SensorEvent 传感器事件,用于获取传感器数据变化时的所有信息
    • wx {number} 绕x轴的角速度,单位弧度/s
    • wy {number} 绕y轴的角速度,单位弧度/s
    • wz {number} 绕z轴的角速度,单位弧度/s
  • magnetic_field 磁场传感器,参数(event, bx, by, bz):
    • event SensorEvent 传感器事件,用于获取传感器数据变化时的所有信息
    • bx {number} x轴上的磁场强度,单位uT
    • by {number} y轴上的磁场强度,单位uT
    • bz {number} z轴上的磁场强度,单位uT
  • gravity 重力传感器,参数(event, gx, gy, gz):
    • event SensorEvent 传感器事件,用于获取传感器数据变化时的所有信息
    • gx {number} x轴上的重力加速度,单位m/s^2
    • gy {number} y轴上的重力加速度,单位m/s^2
    • gz {number} z轴上的重力加速度,单位m/s^2
  • linear_acceleration 线性加速度传感器,参数(event, ax, ay, az):
    • event SensorEvent 传感器事件,用于获取传感器数据变化时的所有信息
    • ax {number} x轴上的线性加速度,单位m/s^2
    • ay {number} y轴上的线性加速度,单位m/s^2
    • az {number} z轴上的线性加速度,单位m/s^2
  • ambient_temperature 环境温度传感器,大部分设备并不支持,参数(event, t):
    • event SensorEvent 传感器事件,用于获取传感器数据变化时的所有信息
    • t {number} 环境温度,单位摄氏度。
  • light 光线传感器,参数(event, light):
    • event SensorEvent 传感器事件,用于获取传感器数据变化时的所有信息
    • light {number} 环境光强度,单位lux
  • pressure 压力传感器,参数(event, p):
    • event SensorEvent 传感器事件,用于获取传感器数据变化时的所有信息
    • p {number} 大气压,单位hPa
  • proximity 距离传感器,参数(event, distance):
    • event SensorEvent 传感器事件,用于获取传感器数据变化时的所有信息
    • distance {number} 一般指设备前置摄像头旁边的距离传感器到前方障碍物的距离,并且很多设备上这个值只有两种情况:当障碍物较近时该值为0,当障碍物较远或在范围内没有障碍物时该值为5
  • relative_humidity 湿度传感器,大部分设备并不支持,参数(event, rh):
    • event SensorEvent 传感器事件,用于获取传感器数据变化时的所有信息
    • rh {number} 相对湿度,范围为0~100(百分比)

sensors.register(sensorName[, delay])

  • sensorName {string} 传感器名称,常用的传感器名称如上面所述
  • delay {number} 传感器数据更新频率,可选,默认为sensors.delay.normal。可用的值如下:
    • sensors.delay.normal 正常频率
    • sensors.delay.ui 适合于用户界面的更新频率
    • sensors.delay.game 适合于游戏的更新频率
    • sensors.delay.fastest 最快的更新频率】
  • 返回 SensorEventEmiiter

注册一个传感器监听并返回SensorEventEmitter

例如:

console.show();

//注册传感器监听

var sensor = sensors.register("gravity");

if(sensor == null){

    toast("不支持重力传感器");

    exit();

}

//监听数据

sensor.on("change", (gx, gy, gz)=>{

    log("重力加速度: %d, %d, %d", gx, gy, gz);

});

可以通过delay参数来指定传感器数据的更新频率,例如:

var sensor = sensors.register("gravity", sensors.delay.game);

另外,如果不支持sensorName所指定的传感器,那么该函数将返回null;但如果sensors.ignoresUnsupportedSensor的值被设置为true, 则该函数会返回一个不会分发任何传感器事件的SensorEventEmitter

例如:

sensors.ignoresUnsupportedSensor = true;

//无需null判断

sensors.register("gravity").on("change", (gx, gy, gz)=>{

    log("重力加速度: %d, %d, %d", gx, gy, gz);

});

更多信息,参见SensorEventEmittersensors.ignoresUnsupportedSensor

sensors.unregister(emitter)

  • emiiter SensorEventEmitter

注销该传感器监听器。被注销的监听器将不再能监听传感器数据。

//注册一个传感器监听器

var sensor = sensors.register("gravity");

if(sensor == null){

    exit();

}

//2秒后注销该监听器

setTimeout(()=> {

    sensors.unregister(sensor);

}, 2000);

sensors.unregisterAll()

注销所有传感器监听器。

sensors.ignoresUnsupportedSensor

  • {boolean}

表示是否忽略不支持的传感器。如果该值被设置为true,则函数sensors.register()即使对不支持的传感器也会返回一个无任何数据的虚拟传感器监听,也就是sensors.register()不会返回null从而避免非空判断,并且此时会触发sensors的"unsupported_sensor"事件。

//忽略不支持的传感器

sensors.ignoresUnsupportedSensor = true;

//监听有不支持的传感器时的事件

sensors.on("unsupported_sensor", function(sensorName){

    toastLog("不支持的传感器: " + sensorName);

});

//随便注册一个不存在的传感器。

log(sensors.register("aaabbb"));

事件: 'unsupported_sensor'

  • sensorName {string} 不支持的传感器名称

当sensors.ignoresUnsupportedSensor被设置为true并且有不支持的传感器被注册时触发该事件。事件参数的传感器名称。

SensorEventEmitter

注册传感器返回的对象,其本身是一个EventEmmiter,用于监听传感器事件。

事件: 'change'

  • ..args {Any} 传感器参数

当传感器数据改变时触发该事件;该事件触发的最高频繁由sensors.register()指定的delay参数决定。

事件参数根据传感器类型不同而不同,具体参见本章最前面的列表。

一个监听光线传感器和加速度传感器并且每0.5秒获取一个数据并最终写入一个csv表格文件的例子如下:

//csv文件路径

cosnt csvPath = "/sdcard/sensors_data.csv";

//记录光线传感器的数据

var light = 0;

//记录加速度传感器的数据

var ax = 0;

var ay = 0;

var az = 0;

//监听光线传感器

sensors.register("light", sensors.delay.fastest)

    .on("change", l => {

        light = l;

    });

//监听加速度传感器

sensors.register("accelerometer", sensors.delay.fastest)

    .on("change", (ax0, ay0, az0) => {

        ax = ax0;

        ay = ay0;

        az = az0;

    });

 

var file = open(csvPath, "w");

//写csv表格头

file.writeline("light,ax,ay,az")

//每0.5秒获取一次数据并写入文件

setInterval(()=>{

    file.writeline(util.format("%d,%d,%d,%d", light, ax, ay, az));

}, 500);

//10秒后退出并打开文件

setTimeout(()=>{

    file.close();

    sensors.unregsiterAll();

    app.viewFile(csvPath);

}, 10 * 1000);

事件: 'accuracy_change'

  • accuracy {number} 表示传感器精度。为以下值之一:
    • -1 传感器未连接
    • 0 传感器不可读
    • 1 低精度
    • 2 中精度
    • 3 高精度

当传感器精度改变时会触发的事件。比较少用。

shell即Unix Shell,在类Unix系统提供与操作系统交互的一系列命令。

很多程序可以用来执行shell命令,例如终端模拟器。

在Auto.js大致等同于用adb执行命令"adb shell"。其实现包括两种方式:

  • 通过java.lang.Runtime.exec执行(shell, Tap, Home等函数)
  • 通过内嵌终端模拟器执行(RootAutomator, Shell等对象)

shell函数

Stability: 2 - Stable

shell(cmd[, root])

  • cmd {string} 要执行的命令
  • root {Boolean} 是否以root权限运行,默认为false。

一次性执行命令cmd, 并返回命令的执行结果。返回对象的其属性如下:

  • code {number} 返回码。执行成功时为0,失败时为非0的数字。
  • result {string} 运行结果(stdout输出结果)
  • error {string} 运行的错误信息(stderr输出结果)。例如执行需要root权限的命令但没有授予root权限会返回错误信息"Permission denied"。

示例(强制停止微信) :

var result = shell("am force-stop com.tencent.mm", true);

log(result);

console.show();

if(result.code == 0){

  toast("执行成功");

}else{

  toast("执行失败!请到控制台查看错误信息");

}

Shell

Stability: 2 - Stable

shell函数通过用来一次性执行单条命令并获取结果。如果有多条命令需要执行,用Shell对象的效率更高。这是因为,每次运行shell函数都会打开一个单独的shell进程并在运行结束后关闭他,这个过程需要一定的时间;而Shell对象自始至终使用同一个shell进程。

new Shell(root)

  • root {Boolean} 是否以root权限运行一个shell进程,默认为false。这将会影响其后使用该Shell对象执行的命令的权限

Shell对象的"构造函数"。

var sh = new Shell(true);

//强制停止微信

sh.exec("am force-stop com.tencent.mm");

sh.exit();

Shell.exec(cmd)

  • cmd {string} 要执行的命令

执行命令cmd。该函数不会返回任何值。

注意,命令执行是"异步"的、非阻塞的。也就是不会等待命令完成后才继续向下执行。

尽管这样的设计使用起来有很多不便之处,但受限于终端模拟器,暂时没有解决方式;如果后续能找到解决方案,则将提供Shell.execAndWaitFor函数。

Shell.exit()

直接退出shell。正在执行的命令会被强制退出。

Shell.exitAndWaitFor()

执行"exit"命令并等待执行命令执行完成、退出shell。

此函数会执行exit命令来正常退出shell。

Shell.setCallback(callback)

  • callback {Object} 回调函数

设置该Shell的回调函数,以便监听Shell的输出。可以包括以下属性:

  • onOutput {Function} 每当shell有新的输出时便会调用该函数。其参数是一个字符串。
  • onNewLine {Function} 每当shell有新的一行输出时便会调用该函数。其参数是一个字符串(不包括最后的换行符)。

例如:

var sh = new Shell();

sh.setCallback({

    onNewLine: function(line){

        //有新的一行输出时打印到控制台

        log(line);

    }

})

while(true){

    //循环输入命令

    var cmd = dialogs.rawInput("请输入要执行的命令,输入exit退出");

    if(cmd == "exit"){

        break;

    }

    //执行命令

    sh.exec(cmd);

}

sh.exit();

附录: shell命令简介

以下关于shell命令的资料来自AndroidStudio用户指南:Shell命令

am命令

am命令即Activity Manager命令,用于管理应用程序活动、服务等。

以下命令均以"am "开头,例如shell('am start -p com.tencent.mm');(启动微信)

start [options] intent

启动 intent 指定的 Activity(应用程序活动)。
请参阅 intent 参数的规范

选项包括:

  • -D:启用调试。
  • -W:等待启动完成。
  • --start-profiler file:启动分析器并将结果发送到 file。
  • -P file:类似于 --start-profiler,但当应用进入空闲状态时分析停止。
  • -R count:重复 Activity 启动 count 次数。在每次重复前,将完成顶部 Activity。
  • -S:启动 Activity 前强行停止目标应用。
  • --opengl-trace:启用 OpenGL 函数的跟踪。
  • --user user_id | current:指定要作为哪个用户运行;如果未指定,则作为当前用户运行。

startservice [options] intent

启动 intent 指定的 Service(服务)。
请参阅 intent 参数的规范
选项包括:

  • --user user_id | current:指定要作为哪个用户运行;如果未指定,则作为当前用户运行。

force-stop package

强行停止与 package(应用包名)关联的所有应用。

kill [options] package

终止与 package(应用包名)关联的所有进程。此命令仅终止可安全终止且不会影响用户体验的进程。
选项包括:

  • --user user_id | all | current:指定将终止其进程的用户;如果未指定,则终止所有用户的进程。

kill-all

终止所有后台进程。

broadcast [options] intent

发出广播 intent。 请参阅 intent 参数的规范

选项包括:

  • [--user user_id | all | current]:指定要发送到的用户;如果未指定,则发送到所有用户。

instrument [options] component

使用 Instrumentation 实例启动监控。通常,目标 component 是表单 test_package/runner_class。
选项包括:

  • -r:输出原始结果(否则对 report_key_streamresult 进行解码)。与 [-e perf true] 结合使用以生成性能测量的原始输出。
  • -e name value:将参数 name 设为 value。对于测试运行器,通用表单为 -e testrunner_flag value[,value...]。
  • -p file:将分析数据写入 file。
  • -w:先等待仪器完成,然后再返回。测试运行器需要使用此选项。
  • --no-window-animation:运行时关闭窗口动画。
  • --user user_id | current:指定仪器在哪个用户中运行;如果未指定,则在当前用户中运行。
  • profile start process file 启动 process 的分析器,将结果写入 file。
  • profile stop process 停止 process 的分析器。

dumpheap [options] process file

转储 process 的堆,写入 file。

选项包括:

  • --user [user_id|current]:提供进程名称时,指定要转储的进程用户;如果未指定,则使用当前用户。
  • -n:转储原生堆,而非托管堆。
  • set-debug-app [options] package 将应用 package 设为调试。

选项包括:

  • -w:应用启动时等待调试程序。
  • --persistent:保留此值。
  • clear-debug-app 使用 set-debug-app 清除以前针对调试用途设置的软件包。

monitor [options] 启动对崩溃或 ANR 的监控。

选项包括:

  • --gdb:在崩溃/ANR 时在给定端口上启动 gdbserv。

screen-compat {on|off} package

控制 package 的屏幕兼容性模式。

display-size [reset|widthxheight]

替换模拟器/设备显示尺寸。此命令对于在不同尺寸的屏幕上测试您的应用非常有用,它支持使用大屏设备模仿小屏幕分辨率(反之亦然)。
示例:

shell("am display-size 1280x800", true);

display-density dpi

替换模拟器/设备显示密度。此命令对于在不同密度的屏幕上测试您的应用非常有用,它支持使用低密度屏幕在高密度环境环境上进行测试(反之亦然)。
示例:

shell("am display-density 480", true);

to-uri intent

将给定的 intent 规范以 URI 的形式输出。 请参阅 intent 参数的规范

to-intent-uri intent

将给定的 intent 规范以 intent:URI 的形式输出。 请参阅 intent 参数的规范。

intent参数的规范

对于采用 intent 参数的 am 命令,您可以使用以下选项指定 intent:

  • -a action
    指定 intent 操作,如“android.intent.action.VIEW”。此指定只能声明一次。
  • -d data_uri
    指定 intent 数据 URI,如“content://contacts/people/1”。此指定只能声明一次。
  • -t mime_type
    指定 intent MIME 类型,如“image/png”。此指定只能声明一次。
  • -c category
    指定 intent 类别,如“android.intent.category.APP_CONTACTS”。
  • -n component
    指定带有软件包名称前缀的组件名称以创建显式 intent,如“com.example.app/.ExampleActivity”。
  • -f flags
    将标志添加到 setFlags() 支持的 intent。
  • --esn extra_key
    添加一个 null extra。URI intent 不支持此选项。
  • -e|--es extra_key extra_string_value
    添加字符串数据作为键值对。
  • --ez extra_key extra_boolean_value
    添加布尔型数据作为键值对。
  • --ei extra_key extra_int_value
    添加整数型数据作为键值对。
  • --el extra_key extra_long_value
    添加长整型数据作为键值对。
  • --ef extra_key extra_float_value
    添加浮点型数据作为键值对。
  • --eu extra_key extra_uri_value
    添加 URI 数据作为键值对。
  • --ecn extra_key extra_component_name_value
    添加组件名称,将其作为 ComponentName 对象进行转换和传递。
  • --eia extra_key extra_int_value[,extra_int_value...]
    添加整数数组。
  • --ela extra_key extra_long_value[,extra_long_value...]
    添加长整型数组。
  • --efa extra_key extra_float_value[,extra_float_value...]
    添加浮点型数组。
  • --grant-read-uri-permission
    包含标志 FLAG_GRANT_READ_URI_PERMISSION。
  • --grant-write-uri-permission
    包含标志 FLAG_GRANT_WRITE_URI_PERMISSION。
  • --debug-log-resolution
    包含标志 FLAG_DEBUG_LOG_RESOLUTION。
  • --exclude-stopped-packages
    包含标志 FLAG_EXCLUDE_STOPPED_PACKAGES。
  • --include-stopped-packages
    包含标志 FLAG_INCLUDE_STOPPED_PACKAGES。
  • --activity-brought-to-front
    包含标志 FLAG_ACTIVITY_BROUGHT_TO_FRONT。
  • --activity-clear-top
    包含标志 FLAG_ACTIVITY_CLEAR_TOP。
  • --activity-clear-when-task-reset
    包含标志 FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET。
  • --activity-exclude-from-recents
    包含标志 FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS。
  • --activity-launched-from-history
    包含标志 FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY。
  • --activity-multiple-task
    包含标志 FLAG_ACTIVITY_MULTIPLE_TASK。
  • --activity-no-animation
    包含标志 FLAG_ACTIVITY_NO_ANIMATION。
  • --activity-no-history
    包含标志 FLAG_ACTIVITY_NO_HISTORY。
  • --activity-no-user-action
    包含标志 FLAG_ACTIVITY_NO_USER_ACTION。
  • --activity-previous-is-top
    包含标志 FLAG_ACTIVITY_PREVIOUS_IS_TOP。
  • --activity-reorder-to-front
    包含标志 FLAG_ACTIVITY_REORDER_TO_FRONT。
  • --activity-reset-task-if-needed
    包含标志 FLAG_ACTIVITY_RESET_TASK_IF_NEEDED。
  • --activity-single-top
    包含标志 FLAG_ACTIVITY_SINGLE_TOP。
  • --activity-clear-task
    包含标志 FLAG_ACTIVITY_CLEAR_TASK。
  • --activity-task-on-home
    包含标志 FLAG_ACTIVITY_TASK_ON_HOME。
  • --receiver-registered-only
    包含标志 FLAG_RECEIVER_REGISTERED_ONLY。
  • --receiver-replace-pending
    包含标志 FLAG_RECEIVER_REPLACE_PENDING。
  • --selector
    需要使用 -d 和 -t 选项以设置 intent 数据和类型。

URI component package

如果不受上述某一选项的限制,您可以直接指定 URI、软件包名称和组件名称。当参数不受限制时,如果参数包含一个“:”(冒号),则此工具假定参数是一个 URI;如果参数包含一个“/”(正斜杠),则此工具假定参数是一个组件名称;否则,此工具假定参数是一个软件包名称。

应用包名

所谓应用包名,是唯一确定应用的标识。例如微信的包名是"com.tencent.mm", QQ的包名是"com.tencent.mobileqq"。
要获取一个应用的包名,可以通过函数getPackageName(appName)获取。参见帮助->其他一般函数。

pm命令

pm命令用于管理应用程序,例如卸载应用、冻结应用等。
以下命令均以"pm "开头,例如"shell(\"pm disable com.tencent.mm\");"(冻结微信)

list packages [options] filter

输出所有软件包,或者,仅输出包名称包含 filter 中的文本的软件包。
选项:

  • -f:查看它们的关联文件。
  • -d:进行过滤以仅显示已停用的软件包。
  • -e:进行过滤以仅显示已启用的软件包。
  • -s:进行过滤以仅显示系统软件包。
  • -3:进行过滤以仅显示第三方软件包。
  • -i:查看软件包的安装程序。
  • -u:也包括卸载的软件包。
  • --user user_id:要查询的用户空间。

list permission-groups

输出所有已知的权限组。

list permissions [options] group

输出所有已知权限,或者,仅输出 group 中的权限。
选项:

  • -g:按组加以组织。
  • -f:输出所有信息。
  • -s:简短摘要。
  • -d:仅列出危险权限。
  • -u:仅列出用户将看到的权限。

list instrumentation [options]

列出所有测试软件包。
选项:

  • -f:列出用于测试软件包的 APK 文件。
  • target_package:列出仅用于此应用的测试软件包。

list features

输出系统的所有功能。

list libraries

输出当前设备支持的所有库。

list users

输出系统上的所有用户。

path package

输出给定 package 的 APK 的路径。

install [options] path

将软件包(通过 path 指定)安装到系统。
选项:

  • -l:安装具有转发锁定功能的软件包。
  • -r:重新安装现有应用,保留其数据。
  • -t:允许安装测试 APK。
  • -i installer_package_name:指定安装程序软件包名称。
  • -s:在共享的大容量存储(如 sdcard)上安装软件包。
  • -f:在内部系统内存上安装软件包。
  • -d:允许版本代码降级。
  • -g:授予应用清单文件中列出的所有权限。

uninstall [options] package

从系统中卸载软件包。
选项:

  • -k:移除软件包后保留数据和缓存目录。

clear package

删除与软件包关联的所有数据。

enable package_or_component

启用给定软件包或组件(作为“package/class”写入)。

disable package_or_component

停用给定软件包或组件(作为“package/class”写入)。

disable-user [options] package_or_component

选项:

  • --user user_id:要停用的用户。

grant package_name permission

向应用授予权限。在运行 Android 6.0(API 级别 23)及更高版本的设备上,可以是应用清单中声明的任何权限。在运行 Android 5.1(API 级别 22)和更低版本的设备上,必须是应用定义的可选权限。

revoke package_name permission

从应用中撤销权限。在运行 Android 6.0(API 级别 23)及更高版本的设备上,可以是应用清单中声明的任何权限。在运行 Android 5.1(API 级别 22)和更低版本的设备上,必须是应用定义的可选权限。

set-install-location location

更改默认安装位置。位置值:

  • 0:自动—让系统决定最佳位置。
  • 1:内部—安装在内部设备存储上。
  • 2:外部—安装在外部介质上。

注:此命令仅用于调试目的;使用此命令会导致应用中断和其他意外行为。

get-install-location

返回当前安装位置。返回值:

  • 0 [auto]:让系统决定最佳位置。
  • 1 [internal]:安装在内部设备存储上
  • 2 [external]:安装在外部介质上

set-permission-enforced permission [true|false]

指定是否应强制执行给定的权限。

trim-caches desired_free_space

减少缓存文件以达到给定的可用空间。

create-user user_name

使用给定的 user_name 创建新用户,输出新用户的标识符。

remove-user user_id

移除具有给定的 user_id 的用户,删除与该用户关联的所有数据。

get-max-users

输出设备支持的最大用户数。

其他命令

进行屏幕截图

screencap 命令是一个用于对设备显示屏进行屏幕截图的 shell 实用程序。在 shell 中,此语法为:

screencap filename

例如:

$ shell("screencap /sdcard/screen.png");

列表文件

ls filepath

例如:

log(shell("ls /system/bin").result);

Storages

Stability: 2 - Stable

 

storages模块提供了保存简单数据、用户配置等的支持。保存的数据除非应用被卸载或者被主动删除,否则会一直保留。

 

storages支持number, boolean, string等数据类型以及把Object, Array用JSON.stringify序列化存取。

 

storages保存的数据在脚本之间是共享的,任何脚本只要知道storage名称便可以获取到相应的数据,因此它不能用于敏感数据的储存。 storages无法像Web开发中LocalStorage一样提供根据域名独立的存储,因为脚本的路径随时可能改变。

 

storages.create(name)

name {string} 本地存储名称

创建一个本地存储并返回一个Storage对象。不同名称的本地存储的数据是隔开的,而相同名称的本地存储的数据是共享的。

 

例如在一个脚本中,创建名称为ABC的存储并存入a=123:

 

var storage = storages.create("ABC");

storage.put("a", 123);

而在另一个脚本中是可以获取到ABC以及a的值的:

 

var storage = storages.create("ABC");

log("a = " + storage.get("a"));

因此,本地存储的名称比较重要,尽量使用含有域名、作者邮箱等唯一信息的名称来避免冲突,例如:

 

var storage = storages.create("[email protected]:ABC");

storages.remove(name)

name {string} 本地存储名称

删除一个本地存储以及他的全部数据。如果该存储不存在,返回false;否则返回true。

 

Storages

Storage.get(key[, defaultValue])

key {string} 键值

defaultValue {any} 可选,默认值

从本地存储中取出键值为key的数据并返回。

 

如果该存储中不包含该数据,这时若指定了默认值参数则返回默认值,否则返回undefined。

 

返回的数据可能是任意数据类型,这取决于使用Storage.put保存该键值的数据时的数据类型。

 

Storage.put(key, value)

key {string} 键值

value {any} 值

把值value保存到本地存储中。value可以是undefined以外的任意数据类型。如果value为undefined则抛出TypeError。

 

存储的过程实际上是使用JSON.stringify把value转换为字符串再保存,因此value必须是可JSON化的才能被接受。

 

Storage.remove(key)

key {string} 键值

移除键值为key的数据。不返回任何值。

 

Storage.contains(key)

key {string} 键值

返回该本地存储是否包含键值为key的数据。是则返回true,否则返回false。

 

Storage.clear()

移除该本地存储的所有数据。不返回任何值。

Threads

Stability: 1 - Experiment

threads模块提供了多线程支持,可以启动新线程来运行脚本。

脚本主线程会等待所有子线程执行完成后才停止执行,因此如果子线程中有死循环,请在必要的时候调用exit()来直接停止脚本或threads.shutDownAll()来停止所有子线程。

通过threads.start()启动的所有线程会在脚本被强制停止时自动停止。

由于JavaScript自身没有多线程的支持,因此您可能会遇到意料之外的问题。

threads.start(action)

  • action {Function} 要在新线程执行的函数
  • 返回 Thread

启动一个新线程并执行action。

例如:

threads.start(function(){

    //在新线程执行的代码

    while(true){

        log("子线程");

    }

});

while(true){

    log("脚本主线程");

}

通过该函数返回的Thread对象可以获取该线程的状态,控制该线程的运行中。例如:

var thread = threads.start(function(){

    while(true){

        log("子线程");

    }

});

//停止线程执行

thread.interrupt();

更多信息参见Thread

threads.shutDownAll()

停止所有通过threads.start()启动的子线程。

threads.currentThread()

  • 返回 Thread

返回当前线程。

threads.disposable()

  • 返回 Disposable

新建一个Disposable对象,用于等待另一个线程的某个一次性结果。更多信息参见线程通信以及Disposable

threads.atomic([initialValue])

  • initialValue {number} 初始整数值,默认为0
  • 返回AtomicLong

新建一个整数原子变量。更多信息参见线程安全以及AtomicLong

threads.lock()

  • 返回ReentrantLock

新建一个可重入锁。更多信息参见线程安全以及ReentrantLock

Thread

线程对象,threads.start()返回的对象,用于获取和控制线程的状态,与其他线程交互等。

Thread对象提供了和timers模块一样的API,例如setTimeout(), setInterval()等,用于在该线程执行相应的定时回调,从而使线程之间可以直接交互。例如:

var thread = threads.start(function(){

    //在子线程执行的定时器

    setInterval(function(){

        log("子线程:" + threads.currentThread());

    }, 1000);

});

 

log("当前线程为主线程:" + threads.currentThread());

 

//等待子线程启动

thread.waitFor();

//在子线程执行的定时器

thread.setTimeout(function(){

    //这段代码会在子线程执行

    log("当前线程为子线程:" + threads.currentThread());

}, 2000);

 

sleep(30 * 1000);

thread.interrupt();

Thread.interrupt()

中断线程运行。

Thread.join([timeout])

  • timeout {number} 等待时间,单位毫秒

等待线程执行完成。如果timeout为0,则会一直等待直至该线程执行完成;否则最多等待timeout毫秒的时间。

例如:

var sum = 0;

//启动子线程计算1加到10000

var thread = threads.start(function(){

    for(var i = 0; i < 10000; i++){

        sum += i;

    }

});

//等待该线程完成

thread.join();

toast("sum = " + sum);

isAlive()

  • 返回 {boolean}

返回线程是否存活。如果线程仍未开始或已经结束,返回false; 如果线程已经开始或者正在运行中,返回true。

waitFor()

等待线程开始执行。调用threads.start()以后线程仍然需要一定时间才能开始执行,因此调用此函数会等待线程开始执行;如果线程已经处于执行状态则立即返回。

var thread = threads.start(function(){

    //do something

});

thread.waitFor();

thread.setTimeout(function(){

    //do something

}, 1000);

Thread.setTimeout(callback, delay[, ...args])

参见timers.setTimeout()

区别在于, 该定时器会在该线程执行。如果当前线程仍未开始执行或已经执行结束,则抛出IllegalStateException。

log("当前线程(主线程):" + threads.currentThread());

 

var thread = threads.start(function(){

    //设置一个空的定时来保持线程的运行状态

    setInterval(function(){}, 1000);

});

 

sleep(1000);

thread.setTimeout(function(){

    log("当前线程(子线程):" + threads.currentThread());

    exit();

}, 1000);

Thread.setInterval(callback, delay[, ...args])

参见timers.setInterval()

区别在于, 该定时器会在该线程执行。如果当前线程仍未开始执行或已经执行结束,则抛出IllegalStateException。

Thread.setImmediate(callback[, ...args])

参见timers.setImmediate()

区别在于, 该定时器会在该线程执行。如果当前线程仍未开始执行或已经执行结束,则抛出IllegalStateException。

Thread.clearInterval(id)

参见timers.clearInterval()

区别在于, 该定时器会在该线程执行。如果当前线程仍未开始执行或已经执行结束,则抛出IllegalStateException。

Thread.clearTimeout(id)

参见timers.clearTimeout()

区别在于, 该定时器会在该线程执行。如果当前线程仍未开始执行或已经执行结束,则抛出IllegalStateException。

Thread.clearImmediate(id)

参见timers.clearImmediate()

区别在于, 该定时器会在该线程执行。如果当前线程仍未开始执行或已经执行结束,则抛出IllegalStateException。

线程安全

线程安全问题是一个相对专业的编程问题,本章节只提供给有需要的用户。

引用维基百科的解释:

线程安全是编程中的术语,指某个函数、函数库在多线程环境中被调用时,能够正确地处理多个线程之间的共享变量,使程序功能正确完成。

在Auto.js中,线程间变量在符合JavaScript变量作用域规则的前提下是共享的,例如全局变量在所有线程都能访问,并且保证他们在所有线程的可见性。但是,不保证任何操作的原子性。例如经典的自增"i++"将不是原子性操作。

Rhino和Auto.js提供了一些简单的设施来解决简单的线程安全问题,如锁threads.lock(), 函数同步锁sync(), 整数原子变量threads.atomic()等。

例如,对于多线程共享下的整数的自增操作(自增操作会导致问题,是因为自增操作实际上为i = i + 1,也就是先读取i的值, 把他加1, 再赋值给i, 如果两个线程同时进行自增操作,可能出现i的值只增加了1的情况),应该使用threads.atomic()函数来新建一个整数原子变量,或者使用锁threads.lock()来保证操作的原子性,或者用sync()来增加同步锁。

线程不安全的代码如下:

var i = 0;

threads.start(function(){

    while(true){

        log(i++);

    }

});

while(true){

    log(i++);

}

此段代码运行后打开日志,可以看到日志中有重复的值出现。

使用threads.atomic()的线程安全的代码如下:

//atomic返回的对象保证了自增的原子性

var i = threads.atomic();

threads.start(function(){

    while(true){

        log(i.getAndIncrement());

    }

});

while(true){

    log(i.getAndIncrement());

}

或者:

//锁保证了操作的原子性

var lock = threads.lock();

var i = 0;

threads.start(function(){

    while(true){

        lock.lock();

        log(i++);

        lock.unlock();

    }

});

while(true){

    lock.lock();

    log(i++);

    lock.unlock();

}

或者:

//sync函数会把里面的函数加上同步锁,使得在同一时刻最多只能有一个线程执行这个函数

var i = 0;

var getAndIncrement = sync(function(){

    return i++;

});

threads.start(function(){

    while(true){

        log(getAndIncrement());

    }

});

while(true){

    log(getAndIncrement());

}

另外,数组Array不是线程安全的,如果有这种复杂的需求,请用Android和Java相关API来实现。例如CopyOnWriteList, Vector等都是代替数组的线程安全的类,用于不同的场景。例如:

var nums = new java.util.Vector();

nums.add(123);

nums.add(456);

toast("长度为" + nums.size());

toast("第一个元素为" + nums.get(0));

但很明显的是,这些类不像数组那样简便易用,也不能使用诸如slice()之类的方便的函数。在未来可能会加入线程安全的数组来解决这个问题。当然您也可以为每个数组的操作加锁来解决线程安全问题:

var nums = [];

var numsLock = threads.lock();

threads.start(function(){

    //向数组添加元素123

    numsLock.lock();

    nums.push(123);

    log("线程: %s, 数组: %s", threads.currentThread(), nums);

    numsLock.unlock();

});

 

threads.start(function(){

    //向数组添加元素456

    numsLock.lock();

    nums.push(456);

    log("线程: %s, 数组: %s", threads.currentThread(), nums);

    numsLock.unlock();

});

 

//删除数组最后一个元素

numsLock.lock();

nums.pop();

log("线程: %s, 数组: %s", threads.currentThread(), nums);

numsLock.unlock();

sync(func)

  • func {Function} 函数
  • 返回 {Function}

给函数func加上同步锁并作为一个新函数返回。

var i = 0;

function add(x){

    i += x;

}

 

var syncAdd = sync(add);

syncAdd(10);

toast(i);

线程通信

Auto.js提供了一些简单的设施来支持简单的线程通信。threads.disposable()用于一个线程等待另一个线程的(一次性)结果,同时Lock.newCondition()提供了Condition对象用于一般的线程通信(await, signal)。另外,events模块也可以用于线程通信,通过指定EventEmiiter的回调执行的线程来实现。

使用threads.disposable()可以简单地等待和获取某个线程的执行结果。例如要等待某个线程计算"1+.....+10000":

var sum = threads.disposable();

//启动子线程计算

threads.start(function(){

    var s = 0;

    //从1加到10000

    for(var i = 1; i <= 10000; i++){

        s += i;

    }

    //通知主线程接收结果

    sum.setAndNotify(s);

});

//blockedGet()用于等待结果

toast("sum = " + sum.blockedGet());

如果上述代码用Condition实现:

//新建一个锁

var lock = threads.lock();

//新建一个条件,即"计算完成"

var complete = lock.newCondition();

var sum = 0;

threads.start(function(){

    //从1加到10000

    for(var i = 1; i <= 10000; i++){

        sum += i;

    }

    //通知主线程接收结果

    lock.lock();

    complete.signal();

    lock.unlock();

});

//等待计算完成

lock.lock();

complete.await();

lock.unlock();

//打印结果

toast("sum = " + sum);

如果上诉代码用events模块实现:

//新建一个emitter, 并指定回调执行的线程为当前线程

var sum = events.emitter(threads.currentThread());

threads.start(function(){

    var s = 0;

    //从1加到10000

    for(var i = 1; i <= 10000; i++){

        s += i;

    }

    //发送事件result通知主线程接收结果

    sum.emit('result', s);

});

sum.on('result', function(s){

    toastLog("sum = " + s + ", 当前线程: " + threads.currentThread());

});

有关线程的其他问题,例如生产者消费者等问题,请用Java相关方法解决,例如java.util.concurrent.BlockingQueue。

Timers

Stability: 2 - Stable

timers 模块暴露了一个全局的 API,用于在某个未来时间段调用调度函数。 因为定时器函数是全局的,所以使用该 API 无需调用 timers.*

Auto.js 中的计时器函数实现了与 Web 浏览器提供的定时器类似的 API,除了它使用了一个不同的内部实现,它是基于 Android Looper-Handler消息循环机制构建的。其实现机制与Node.js比较相似。

例如,要在5秒后发出消息"hello":

setTimeout(function(){

    toast("hello")

}, 5000);

需要注意的是,这些定时器仍然是单线程的。如果脚本主体有耗时操作或死循环,则设定的定时器不能被及时执行,例如:

setTimeout(function(){

    //这里的语句会在15秒后执行而不是5秒后

    toast("hello")

}, 5000);

//暂停10秒

sleep(10000);

再如:

setTimeout(function(){

    //这里的语句永远不会被执行

    toast("hello")

}, 5000);

//死循环

while(true);

setInterval(callback, delay[, ...args])

  • callback {Function} 当定时器到点时要调用的函数。
  • delay {number} 调用 callback 之前要等待的毫秒数。
  • ...args {any} 当调用 callback 时要传入的可选参数。

预定每隔 delay 毫秒重复执行的 callback。 返回一个用于 clearInterval() 的 id。

当 delay 小于 0 时,delay 会被设为 0。

setTimeout(callback, delay[, ...args])

  • callback {Function} 当定时器到点时要调用的函数。
  • delay {number} 调用 callback 之前要等待的毫秒数。
  • ...args {any} 当调用 callback 时要传入的可选参数。

预定在 delay 毫秒之后执行的单次 callback。 返回一个用于 clearTimeout() 的 id。

callback 可能不会精确地在 delay 毫秒被调用。 Auto.js 不能保证回调被触发的确切时间,也不能保证它们的顺序。 回调会在尽可能接近所指定的时间上调用。

当 delay 小于 0 时,delay 会被设为 0。

setImmediate(callback[, ...args])

  • callback {Function} 在Looper循环的当前回合结束时要调用的函数。
  • ...args {any} 当调用 callback 时要传入的可选参数。

预定立即执行的 callback,它是在 I/O 事件的回调之后被触发。 返回一个用于 clearImmediate() 的 id。

当多次调用 setImmediate() 时,callback 函数会按照它们被创建的顺序依次执行。 每次事件循环迭代都会处理整个回调队列。 如果一个立即定时器是被一个正在执行的回调排入队列的,则该定时器直到下一次事件循环迭代才会被触发。

setImmediate()、setInterval() 和 setTimeout() 方法每次都会返回表示预定的计时器的id。 它们可用于取消定时器并防止触发。

clearInterval(id)

  • id {number} 一个 setInterval() 返回的 id。

取消一个由 setInterval() 创建的循环定时任务。

例如:

//每5秒就发出一次hello

var id = setInterval(function(){

    toast("hello");

}, 5000);

//1分钟后取消循环

setTimeout(function(){

    clearInterval(id);

}, 60 * 1000);

clearTimeout(id)

  • id {number} 一个 setTimeout() 返回的 id。

取消一个由 setTimeout() 创建的定时任务。

clearImmediate(id)

  • id {number} 一个 setImmediate() 返回的 id。

取消一个由 setImmediate() 创建的 Immediate 对象。

用户界面: UI

ui模块提供了编写用户界面的支持。

带有ui的脚本的的最前面必须使用"ui";指定ui模式,否则脚本将不会以ui模式运行。正确示范:

"ui";

 

//脚本的其他代码

字符串"ui"的前面可以有注释、空行和空格[v4.1.0新增],但是不能有其他代码。

界面是由视图(View)组成的。View分成两种,控件(Widget)和布局(Layout)。控件(Widget)用来具体显示文字、图片、网页等,比如文本控件(text)用来显示文字,按钮控件(button)则可以显示一个按钮并提供点击效果,图片控件(img)则用来显示来自网络或者文件的图片,除此之外还有输入框控件(input)、进度条控件(progressbar)、单选复选框控件(checkbox)等;布局(Layout)则是装着一个或多个控件的"容器",用于控制在他里面的控件的位置,比如垂直布局(vertical)会把他里面的控件从上往下依次显示(即纵向排列),水平布局(horizontal)则会把他里面的控件从左往右依次显示(即横向排列),以及帧布局(frame),他会把他里面的控件直接在左上角显示,如果有多个控件,后面的控件会重叠在前面的控件上。

我们使用xml来编写界面,并通过ui.layout()函数指定界面的布局xml。举个例子:

"ui";

ui.layout(

    

        

你可能感兴趣的:(脚本开发,autoJs,auto.js,Auto.js,安卓自动,Auto打包插件,Auto.js软件,Auto.js合集,Auto.js_4.1.1,Al,auto.js)