Auto.js 3.0.0 文档


目录

App#

app模块提供一系列函数,用于使用其他应用、与其他应用交互。例如发送意图、打开文件、发送邮件等。

同时提供了方便的进阶函数startActivity和sendBroadcast,用他们可完成app模块没有内置的和其他应用的交互。

app.launchApp(appName)#

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

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

launchApp("Auto.js");

app.launch(packageName)#

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

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

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

app.launchPackage(packageName)#

相当于app.launch(packageName)

app.getPackageName(appName)#

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

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

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

app.getAppName(packageName)#

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

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

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

app.openAppSetting(packageName)#

打开应用的详情页(设置页)。如果找不到该应用,返回false; 否则返回true。

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

app.viewFile(path)#

用其他应用查看文件。文件不存在的情况由查看文件的应用处理。

如果找不出可以查看该文件的应用,则抛出ActivityNotException

//查看文本文件
app.viewFile("/sdcard/1.txt");

app.editFile(path)#

用其他应用编辑文件。文件不存在的情况由编辑文件的应用处理。

如果找不出可以编辑该文件的应用,则抛出ActivityNotException

//编辑文本文件
app.editFile("/sdcard/1.txt/);

app.uninstall(packageName)#

卸载应用。执行后会会弹出卸载应用的提示框。如果该包名的应用未安装,由应用卸载程序处理,可能弹出"未找到应用"的提示。

//卸载QQ
app.uninstall("com.tencent.mobileqq");

app.openUrl(url)#

用浏览器打开网站url。

如果没有安装浏览器应用,则抛出ActivityNotException

app.sendEmail(options)#

  • options <Object> 发送邮件的参数。包括:
    • email <string> | <Array> 收件人的邮件地址。如果有多个收件人,则用字符串数组表示
    • cc <string> | <Array> 抄送收件人的邮件地址。如果有多个抄送收件人,则用字符串数组表示
    • bcc <string> | <Array> 密送收件人的邮件地址。如果有多个密送收件人,则用字符串数组表示
    • subject <string> 邮件主题(标题)
    • text <string> 邮件正文
    • attachment <string> 附件的路径。

根据选项options调用邮箱应用发送邮件。这些选项均是可选的。

如果没有安装邮箱应用,则抛出ActivityNotException

//发送邮件给10086@qq.com和10001@qq.com。
app.sendEmail({
    email: ["10086@qq.com", "10001@qq.com"],
    subject: "这是一个邮件标题",
    text: "这是邮件正文"
});

进阶: 意图Intent#

Intent(意图) 是一个消息传递对象,您可以使用它从其他应用组件请求操作。尽管 Intent 可以通过多种方式促进组件之间的通信,但其基本用例主要包括以下三个:

  • 启动活动(Activity): Activity 表示应用中的一个"屏幕"。例如应用主入口都是一个Activity,应用的功能通常也以Activity的形式独立,例如微信的主界面、朋友圈、聊天窗口都是不同的Activity。通过将 Intent 传递给 startActivity(),您可以启动新的 Activity 实例。Intent 描述了要启动的 Activity,并携带了任何必要的数据。

  • 启动服务(Service): Service 是一个不使用用户界面而在后台执行操作的组件。通过将 Intent 传递给 startService(),您可以启动服务执行一次性操作(例如,下载文件)。Intent 描述了要启动的服务,并携带了任何必要的数据。

  • 传递广播: 广播是任何应用均可接收的消息。系统将针对系统事件(例如:系统启动或设备开始充电时)传递各种广播。通过将 Intent 传递给 sendBroadcast()、sendOrderedBroadcast() 或 sendStickyBroadcast(),您可以将广播传递给其他应用。

本模块提供了构建Intent的函数(app.intent()), 启动Activity的函数app.startActivity(), 发送广播的函数app.sendBroadcast()

使用这些方法可以用来方便的调用其他应用。例如直接打开某个QQ号的个人卡片页,打开某个QQ号的聊天窗口等。


app.intent(options)#

  • options <Object> 选项,包括:
    • action <string> 意图的Action,指意图要完成的动作,是一个字符串常量,比如"android.intent.action.SEND"。当action以"android.intent.action"开头时,可以省略前缀,直接用"SEND"代替。常见的action参见常用的意图动作
    • type <string> 意图的MimeType,表示和该意图直接相关的数据的类型,表示比如"text/plain"为纯文本类型。
    • data <string> 意图的Data,表示和该意图直接相关的数据,是一个Uri, 可以是文件路径或者Url等。例如要打开一个文件, action为"android.intent.action.VIEW", data为"file:///sdcard/1.txt"。
    • category <Array> 意图的类别。比较少用。
    • packageName <string> 目标包名
    • className <string> 目标Activity或Service等组件的名称
    • extras <Object> 以键值对构成的这个Intent的Extras(额外信息)。提供该意图的其他信息,例如发送邮件时的邮件标题、邮件正文。

根据选项,构造一个意图Intent对象。

例如:

//打开应用来查看图片文件
var i = app.intent({
    action: "VIEW",
    type: "image/png",
    data: "file:///sdcard/1.png"
});
app.startAcvitity(i);

更多信息,请百度安卓Intent或参考Android指南: Intent

app.startActivity(options)#

根据选项构造一个Intent,并启动该Activity。

app.sendBroadcast(options)#

根据选项构造一个Intent,并发送该广播。

Console#

Stability: 2 - Stable

控制台模块提供了一个和Web浏览器中相似的用于调试的控制台。用于输出一些调试信息、中间结果等。 console模块中的一些函数也可以直接作为全局函数使用,例如log, print等。

console.show()#

显示控制台。这会显示一个控制台的悬浮窗(需要悬浮窗权限)。

console.hide()#

隐藏控制台悬浮窗。

console.clear()#

清空控制台。

console.log([data][, ...args])#

  • data
  • ...args

打印到控制台,并带上换行符。 可以传入多个参数,第一个参数作为主要信息,其他参数作为类似于 printf(3) 中的代替值(参数都会传给 util.format())。

const count = 5;
console.log('count: %d', count);
// 打印: count: 5 到 stdout
console.log('count:', count);
// 打印: count: 5 到 stdout

详见 util.format()。

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

console.verbose([data][, ...args])#

  • data
  • ...args

与console.log类似,但输出结果以灰色字体显示。输出优先级低于log,用于输出观察性质的信息。

console.info([data][, ...args])#

  • data
  • ...args

与console.log类似,但输出结果以绿色字体显示。输出优先级高于log, 用于输出重要信息。

console.warn([data][, ...args])#

  • data
  • ...args

与console.log类似,但输出结果以蓝色字体显示。输出优先级高于info, 用于输出警告信息。

console.error([data][, ...args])#

  • data
  • ...args

与console.log类似,但输出结果以红色字体显示。输出优先级高于warn, 用于输出错误信息。

console.assert(value, message)#

  • value <any> 要断言的布尔值
  • message <string> value为false时要输出的信息

断言。如果value为false则输出错误信息message并停止脚本运行。

var a = 1 + 1;
console.assert(a == 2, "加法出错啦");

console.input(data[, ...args])#

  • data
  • ...args

与console.log一样输出信息,并在控制台显示输入框等待输入。按控制台的确认按钮后会将输入的字符串用eval计算后返回。

部分机型可能会有控制台不显示输入框的情况,属于bug。

例如:

var n = console.input("请输入一个数字:"); 
//输入123之后:
toast(n + 1);
//显示124

console.rawInput(data[, ...args])#

  • data
  • ...args

与console.log一样输出信息,并在控制台显示输入框等待输入。按控制台的确认按钮后会将输入的字符串直接返回。

部分机型可能会有控制台不显示输入框的情况,属于bug。

例如:

var n = console.rawInput("请输入一个数字:"); 
//输入123之后:
toast(n + 1);
//显示1231

console.setSize(w, h)#

设置控制台的大小,单位像素。

console.show();
//设置控制台大小为屏幕的四分之一
console.setSize(device.width / 2, device.height / 2);

console.setPosition(x, y)#

设置控制台的位置,单位像素。

console.show();
console.setPosition(100, 100);

print(text)#

相当于log(text)

基于坐标的触摸模拟#

Stability: 2 - Stable

本章节介绍了一些使用坐标进行点击、滑动的函数。这些函数有的需要安卓7.0以上,有的需要root权限。

要获取要点击的位置的坐标,可以在开发者选项中开启"指针位置"。

基于坐标的脚本通常会有分辨率的问题,这时可以通过setScreenMetrics()函数来进行自动坐标放缩。这个函数会影响本章节的所有点击、长按、滑动等函数。通过设定脚本设计时的分辨率,使得脚本在其他分辨率下自动放缩坐标。

控件和坐标也可以相互结合。一些控件是无法点击的(clickable为false), 无法通过.click()函数来点击,这时如果安卓版本在7.0以上或者有root权限,就可以通过以下方式来点击:

//获取这个控件
var widget = id("xxx").findOne();
//获取其中心位置并点击
click(widget.bounds().centerX(), widget.bounds().centerY());
//如果用root权限则用Tap

setScreenMetrics(width, height)#

  • width <number> 屏幕宽度,单位像素
  • height <number> 屏幕高度,单位像素

设置脚本坐标点击所适合的屏幕宽高。如果脚本运行时,屏幕宽度不一致会自动放缩坐标。

例如在1920*1080的设备中,某个操作的代码为

setScreenMetrics(1080, 1920);
click(800, 200);
longClick(300, 500);

那么在其他设备上AutoJs会自动放缩坐标以便脚本仍然有效。例如在540 * 960的屏幕中click(800, 200)实际上会点击位置(400, 100)。

安卓7.0以上的触摸和手势模拟#

Stability: 2 - Stable

注意以下命令只有Android7.0及以上才有效

click(x, y)#

模拟点击坐标(x, y),并返回是否点击成功。只有在点击执行完成后脚本才继续执行。

一般而言,只有点击过程(大约150毫秒)中被其他事件中断(例如用户自行点击)才会点击失败。

使用该函数模拟连续点击时可能有点击速度过慢的问题,这时可以用press()函数代替。

longClick(x, y)#

模拟长按坐标(x, y), 并返回是否成功。只有在长按执行完成(大约600毫秒)时脚本才会继续执行。

一般而言,只有长按过程中被其他事件中断(例如用户自行点击)才会长按失败。

press(x, y, duration)#

  • x <number> 要按住的坐标的x值
  • y <number> 要按住的坐标的y值
  • duration <number> 按住时长,单位毫秒

模拟按住坐标(x, y), 并返回是否成功。只有按住操作执行完成时脚本才会继续执行。

如果按住时间过短,那么会被系统认为是点击;如果时长超过500毫秒,则认为是长按。

一般而言,只有按住过程中被其他事件中断才会操作失败。

一个连点器的例子如下:

//循环100次
for(var i = 0; i < 100; i++){
  //点击位置(500, 1000), 每次用时1毫秒
  press(500, 1000, 1);
}

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

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

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

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

gesture(duration, [x1, y1], [x2, y2], ...)#

  • duration <number> 手势的时长
  • [x, y] ... 手势滑动路径的一系列坐标

模拟手势操作。例如gesture(1000, [0, 0], [500, 500], [500, 1000])为模拟一个从(0, 0)到(500, 500)到(500, 100)的手势操作,时长为2秒。

gestures([delay1, duration1, [x1, y1], [x2, y2], ...], [delay2, duration2, [x3, y3], [x4, y4], ...], ...)#

同时模拟多个手势。每个手势的参数为[delay, duration, 坐标], delay为延迟多久(毫秒)才执行该手势;duration为手势执行时长;坐标为手势经过的点的坐标。其中delay参数可以省略,默认为0。

例如手指捏合:

gestures([0, 500, [800, 300], [500, 1000]],
         [0, 500, [300, 1500], [500, 1000]]);

RootAutomator#

Stability: 2 - Stable

RootAutomator是一个使用root权限来模拟触摸的对象,用它可以完成触摸与多点触摸,并且这些动作的执行没有延迟。

一个脚本中最好只存在一个RootAutomator,并且保证脚本结束退出他。可以在exit事件中退出RootAutomator,例如:

var ra = new RootAutomator();
events.on('exit', function(){
  ra.exit();
});
//执行一些点击操作
...

注意以下命令需要root权限

RootAutomator.tap(x, y[, id])#

  • x <number> 横坐标
  • y <number> 纵坐标
  • id <number> 多点触摸id,可选,默认为1,可以通过setDefaultId指定。

点击位置(x, y)。其中id是一个整数值,用于区分多点触摸,不同的id表示不同的"手指",例如:

var ra = new RootAutomator();
//让"手指1"点击位置(100, 100)
ra.tap(100, 100, 1);
//让"手指2"点击位置(200, 200);
ra.tap(200, 200, 2);
ra.exit();

如果不需要多点触摸,则不需要id这个参数。 多点触摸通常用于手势或游戏操作,例如模拟双指捏合、双指上滑等。

某些情况下可能存在tap点击无反应的情况,这时可以用RootAutomator.press()函数代替。

RootAutomator.swipe(x1, x2, y1, y2[, duration, id])#

  • x1 <number> 滑动起点横坐标
  • y1 <number> 滑动起点纵坐标
  • x2 <number> 滑动终点横坐标
  • y2 <number> 滑动终点纵坐标
  • duration <number> 滑动时长,单位毫秒,默认值为300
  • id <number> 多点触摸id,可选,默认为1

模拟一次从(x1, y1)到(x2, y2)的时间为duration毫秒的滑动。

RootAutomator.press(x, y, duration[, id])#

模拟按下位置(x, y),时长为duration毫秒。

RootAutomator.longPress(x, y[\, id])#

模拟长按位置(x, y)。

以上为简单模拟触摸操作的函数。如果要模拟一些复杂的手势,需要更底层的函数。

RootAutomator.touchDown(x, y[, id])#

模拟手指按下位置(x, y)。

RootAutomator.touchMove(x, y[, id])#

模拟移动手指到位置(x, y)。

RootAutomator.touchUp([id])#

  • id <number> 多点触摸id,可选,默认为1

模拟手指弹起。

使用root权限点击和滑动的简单命令#

Stability: 1 - Experimental

注意:本章节的函数在后续版本很可能有改动!请勿过分依赖本章节函数的副作用。推荐使用RootAutomator代替本章节的触摸函数。

以下函数均需要root权限,可以实现任意位置的点击、滑动等。

  • 这些函数通常首字母大写以表示其特殊的权限。
  • 这些函数均不返回任何值。
  • 并且,这些函数的执行是异步的、非阻塞的,在不同机型上所用的时间不同。脚本不会等待动作执行完成才继续执行。因此最好在每个函数之后加上适当的sleep来达到期望的效果。

例如:

Tap(100, 100);
sleep(500);

注意,动作的执行可能无法被停止,例如:

for(var i = 0; i < 100; i++){
  Tap(100, 100);
}

这段代码执行后可能会出现在任务管理中停止脚本后点击仍然继续的情况。 因此,强烈建议在每个动作后加上延时:

for(var i = 0; i < 100; i++){
  Tap(100, 100);
  sleep(500);
}

Tap(x, y)#

点击位置(x, y), 您可以通过"开发者选项"开启指针位置来确定点击坐标。

Swipe(x1, y1, x2, y2, [duration])#

  • x1, y1 <number> 滑动起点的坐标
  • x2, y2 <number> 滑动终点的坐标
  • duration <number> 滑动动作所用的时间

滑动。从(x1, y1)位置滑动到(x2, y2)位置。

Device#

Stability: 2 - Stable

device模块提供了与设备有关的信息与操作,例如获取设备宽高,内存使用率,IMEI,调整设备亮度、音量等。

此模块的部分函数,例如调整音量,需要"修改系统设置"的权限。如果没有该权限,会抛出SecurityException并跳转到权限设置界面。

device.width#

设备屏幕分辨率宽度。例如1080。

device.height#

设备屏幕分辨率高度。例如1920。

device.buildId#

Either a changelist number, or a label like "M4-rc20".

修订版本号,或者诸如"M4-rc20"的标识。

device.broad#

The name of the underlying board, like "goldfish".

设备的主板(?)型号。

device.brand#

The consumer-visible brand with which the product/hardware will be associated, if any.

与产品或硬件相关的厂商品牌,如"Xiaomi", "Huawei"等。

device.device#

The name of the industrial design.

设备在工业设计中的名称。

deivce.model#

The end-user-visible name for the end product.

设备型号。

device.product#

The name of the overall product.

整个产品的名称。

device.bootloader#

The system bootloader version number.

设备Bootloader的版本。

device.hardware#

The name of the hardware (from the kernel command line or /proc).

设备的硬件名称(来自内核命令行或者/proc)。

device.fingerprint#

A string that uniquely identifies this build. Do not attempt to parse this value.

构建(build)的唯一标识码。

device.serial#

A hardware serial number, if available. Alphanumeric only, case-insensitive.

硬件序列号。

device.sdkInt#

The user-visible SDK version of the framework; its possible values are defined in Build.VERSION_CODES.

安卓系统API版本。例如安卓4.4的sdkInt为19。

device.incremental#

The internal value used by the underlying source control to represent this build. E.g., a perforce changelist number or a git hash.

device.release#

The user-visible version string. E.g., "1.0" or "3.4b5".

Android系统版本号。例如"5.0", "7.1.1"。

device.baseOS#

The base OS build the product is based on.

device.securityPatch#

The user-visible security patch level.

安全补丁程序级别。

device.codename#

The current development codename, or the string "REL" if this is a release build.

开发代号,例如发行版是"REL"。

device.getIMEI()#

返回设备的IMEI.

device.getAndroidId()#

返回设备的Android ID。

Android ID为一个用16进制字符串表示的64位整数,在设备第一次使用时随机生成,之后不会更改,除非恢复出厂设置。

device.getMacAddress()#

返回设备的Mac地址。该函数需要在有WLAN连接的情况下才能获取,否则会返回null。

可能的后续修改:未来可能增加有root权限的情况下通过root权限获取,从而在没有WLAN连接的情况下也能返回正确的Mac地址,因此请勿使用此函数判断WLAN连接。

device.getBrightness()#

返回当前的(手动)亮度。范围为0~255。

device.getBrightnessMode()#

返回当前亮度模式,0为手动亮度,1为自动亮度。

device.setBrightness(b)#

设置当前手动亮度。如果当前是自动亮度模式,该函数不会影响屏幕的亮度。

此函数需要"修改系统设置"的权限。如果没有该权限,会抛出SecurityException并跳转到权限设置界面。

device.setBrightnessMode(mode)#

  • mode <number> 亮度模式,0为手动亮度,1为自动亮度

设置当前亮度模式。

此函数需要"修改系统设置"的权限。如果没有该权限,会抛出SecurityException并跳转到权限设置界面。

device.getMusicVolume()#

返回当前媒体音量。

device.getNotificationVolume()#

返回当前通知音量。

device.getAlarmVolume()#

返回当前闹钟音量。

device.getMusicMaxVolume()#

返回媒体音量的最大值。

device.getNotificationMaxVolume()#

返回通知音量的最大值。

device.getAlarmMaxVolume()#

返回闹钟音量的最大值。

device.setMusicVolume(volume)#

设置当前媒体音量。

此函数需要"修改系统设置"的权限。如果没有该权限,会抛出SecurityException并跳转到权限设置界面。

device.setNotificationVolume(volume)#

设置当前通知音量。

此函数需要"修改系统设置"的权限。如果没有该权限,会抛出SecurityException并跳转到权限设置界面。

device.setAlarmVolume(volume)#

设置当前闹钟音量。

此函数需要"修改系统设置"的权限。如果没有该权限,会抛出SecurityException并跳转到权限设置界面。

device.getBattery()#

返回当前电量百分比。

device.isCharging()#

返回设备是否正在充电。

device.getTotalMem()#

返回设备内存总量,单位字节(B)。1MB = 1024 * 1024B。

device.getAvailMem()#

返回设备当前可用的内存,单位字节(B)。

Dialogs#

Stability: 2 - Stable

dialogs 模块提供了简单的对话框支持,可以通过对话框和用户进行交互。最简单的例子如下:

alert("您好");

这段代码会弹出一个消息提示框显示"您好",并在用户点击"确定"后继续运行。稍微复杂一点的例子如下:

var clear = confirm("要清除所有缓存吗?");
if(clear){
    alert("清除成功!");
}

confirm()会弹出一个对话框并让用户选择"是"或"否",如果选择"是"则返回true。

需要特别注意的是,对话框在ui模式下不能像通常那样使用,应该使用回调函数或者Promise的形式。理解这一点可能稍有困难。举个例子:

"ui";
//回调形式
 confirm("要清除所有缓存吗?", function(clear){
     if(clear){
          alert("清除成功!");
     }
 });
//Promise形式
confirm("要清除所有缓存吗?")
    .then(clear => {
        if(clear){
          alert("清除成功!");
        }
    });

dialogs.alert(title[, content, callback])#

  • title <string> 对话框的标题。
  • content <string> 可选,对话框的内容。默认为空。
  • callback <Function> 回调函数,可选。当用户点击确定时被调用,一般用于ui模式。

显示一个只包含“确定”按钮的提示对话框。直至用户点击确定脚本才继续运行。

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

alert("出现错误~", "出现未知错误,请联系脚本作者”);

在ui模式下该函数返回一个Promise。例如:

"ui";
alert("嘿嘿嘿").then(()=>{
    //当点击确定后会执行这里
});

dialogs.confirm(title[, content, callback])#

  • title <string> 对话框的标题。
  • content <string> 可选,对话框的内容。默认为空。
  • callback <Function> 回调函数,可选。当用户点击确定时被调用,一般用于ui模式。

显示一个包含“确定”和“取消”按钮的提示对话框。如果用户点击“确定”则返回 true ,否则返回 false

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

在ui模式下该函数返回一个Promise。例如:

"ui";
confirm("确定吗").then(value=>{
    //当点击确定后会执行这里, value为true或false, 表示点击"确定"或"取消"
});

dialogs.rawInput(title[, prefill, callback])#

  • title <string> 对话框的标题。
  • prefill <string> 输入框的初始内容,可选,默认为空。
  • callback <Function> 回调函数,可选。当用户点击确定时被调用,一般用于ui模式。

显示一个包含输入框的对话框,等待用户输入内容,并在用户点击确定时将输入的字符串返回。如果用户取消了输入,返回null。

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

var name = rawInput("请输入您的名字", "小明");
alert("您的名字是" + name);

在ui模式下该函数返回一个Promise。例如:

"ui";
rawInput("请输入您的名字", "小明").then(name => {
    alert("您的名字是" + name);
});

当然也可以使用回调函数,例如:

rawInput("请输入您的名字", "小明", name => {
     alert("您的名字是" + name);
});

dialogs.input(title[, prefill, callback])#

等效于 eval(dialogs.rawInput(title, prefill, callback)), 该函数和rawInput的区别在于,会把输入的字符串用eval计算一遍再返回,返回的可能不是字符串。

可以用该函数输入数字、数组等。例如:

var age = dialogs.input("请输入您的年龄", "18");
// new Date().getYear() + 1900 可获取当前年份
var year = new Date().getYear() + 1900 - age;
alert("您的出生年份是" + year);

在ui模式下该函数返回一个Promise。例如:

"ui";
dialogs.input("请输入您的年龄", "18").then(age => {
    var year = new Date().getYear() + 1900 - age;
    alert("您的出生年份是" + year);
});

dialogs.prompt(title[, prefill, callback])#

相当于 dialogs.rawInput();

dialogs.select(title, items, callback)#

  • title <string> 对话框的标题。
  • items <Array> 对话框的选项列表,是一个字符串数组。
  • callback <Function> 回调函数,可选。当用户点击确定时被调用,一般用于ui模式。

显示一个带有选项列表的对话框,等待用户选择,返回用户选择的选项索引(0 ~ item.length - 1)。如果用户取消了选择,返回-1。

var options = ["选项A", "选项B", "选项C", "选项D"]
var i = dialogs.select("请选择一个选项", options);
if(i >= 0){
    toast("您选择的是" + options[i]);
}else{
    toast("您取消了选择");
}

在ui模式下该函数返回一个Promise。例如:

"ui";
dialogs.select("请选择一个选项", ["选项A", "选项B", "选项C", "选项D"])
    .then(i => {
        toast(i);
    });

dialogs.singleChoice(title, items[, index, callback])#

  • title <string> 对话框的标题。
  • items <Array> 对话框的选项列表,是一个字符串数组。
  • index <number> 对话框的初始选项的位置,默认为0。
  • callback <Function> 回调函数,可选。当用户点击确定时被调用,一般用于ui模式。

显示一个单选列表对话框,等待用户选择,返回用户选择的选项索引(0 ~ item.length - 1)。如果用户取消了选择,返回-1。

在ui模式下该函数返回一个Promise

dialogs.multiChoice(title, items[, indices, callback])#

  • title <string> 对话框的标题。
  • items <Array> 对话框的选项列表,是一个字符串数组。
  • indices <Array> 选项列表中初始选中的项目索引的数组,默认为空数组。
  • callback <Function> 回调函数,可选。当用户点击确定时被调用,一般用于ui模式。

显示一个多选列表对话框,等待用户选择,返回用户选择的选项索引的数组。如果用户取消了选择,返回[]

在ui模式下该函数返回一个Promise

Engines#

Stability: 2 - Stable

engines模块包含了一些与脚本环境、脚本运行、脚本引擎有关的函数,包括运行其他脚本,关闭脚本等。

例如,获取脚本所在目录:

toast(engines.myEngine().cwd());

engines.execScript(name, script[, config])#

  • name <string> 要运行的脚本名称。这个名称和文件名称无关,只是在任务管理中显示的名称。
  • script <string> 要运行的脚本内容。
  • config <Object> 运行配置项
    • delay <number> 延迟执行的毫秒数,默认为0
    • loopTimes <number> 循环运行次数,默认为1。0为无限循环。
    • interval <number> 循环运行时两次运行之间的时间间隔,默认为0
    • path <Array> | <string> 指定脚本运行的目录。这些路径会用于require时寻找模块文件。

在新的脚本环境中运行脚本script。返回一个ScriptExectuion对象。

所谓新的脚本环境,指定是,脚本中的变量和原脚本的变量是不共享的,并且,脚本会在新的线程中运行。

最简单的例子如下:

engines.execScript("hello world", "toast('hello world')");

如果要循环运行,则:

//每隔3秒运行一次脚本,循环10次
engines.execScript("hello world", "toast('hello world')", {
    loopTimes: 10,
    interval: 3000
});

用字符串来编写脚本非常不方便,可以结合 Function.toString()的方法来执行特定函数:

function helloWorld(){
    //注意,这里的变量和脚本主体的变量并不共享
    toast("hello world");
}
engines.execScript("hello world", "helloWorld();\n" + helloWorld.toString());

如果要传递变量,则可以把这些封装成一个函数:

function exec(action, args){
    args = args || {};
    engines.execScript(action.name, action + "(" + JSON.stringify(args) + ");\n" + action.toString());
}

//要执行的函数,是一个简单的加法
function add(args){
    toast(args.a + args.b);
}

//在新的脚本环境中执行 1 + 2
exec(add, {a: 1, b:2});

engines.execScriptFile(path[, config])#

  • path <string> 要运行的脚本路径。
  • config <Object> 运行配置项
    • delay <number> 延迟执行的毫秒数,默认为0
    • loopTimes <number> 循环运行次数,默认为1。0为无限循环。
    • interval <number> 循环运行时两次运行之间的时间间隔,默认为0
    • path <Array> | <string> 指定脚本运行的目录。这些路径会用于require时寻找模块文件。

在新的脚本环境中运行脚本文件path。返回一个ScriptExecution对象。

engines.execScriptFile("/sdcard/脚本/1.js");

engines.execAutoFile(path[, config])#

  • path <string> 要运行的录制文件路径。
  • config <Object> 运行配置项
    • delay <number> 延迟执行的毫秒数,默认为0
    • loopTimes <number> 循环运行次数,默认为1。0为无限循环。
    • interval <number> 循环运行时两次运行之间的时间间隔,默认为0
    • path <Array> | <string> 指定脚本运行的目录。这些路径会用于require时寻找模块文件。

在新的脚本环境中运行录制文件path。返回一个ScriptExecution对象。

engines.execAutoFile("/sdcard/脚本/1.auto");

engines.stopAll()#

停止所有正在运行的脚本。包括当前脚本自身。

engines.stopAllAndToast()#

停止所有正在运行的脚本并显示停止的脚本数量。包括当前脚本自身。

engines.myEngine()#

返回当前脚本的脚本引擎对象(ScriptEngine)

ScriptExecution#

执行脚本时返回的对象,可以通过他获取执行的引擎、配置等,也可以停止这个执行。

要停止这个脚本的执行,使用exectuion.getEngine().forceStop().

ScriptExecution.getEngine()#

返回执行该脚本的脚本引擎对象(ScriptEngine)

ScriptExecution.getConfig()#

返回该脚本的运行配置(ScriptConfig)

ScriptEngine#

脚本引擎对象。

ScriptEngine.forceStop()#

停止脚本引擎的执行。

ScriptEngine.cwd()#

返回脚本执行的路径。对于一个脚本文件而言为这个脚本所在的文件夹;对于其他脚本,例如字符串脚本,则为null或者执行时的设置值。

ScriptConfig#

脚本执行时的配置。

delay#

延迟执行的毫秒数

interval#

循环运行时两次运行之间的时间间隔

loopTimes#

循环运行次数

getPath()#

返回一个字符串数组表示脚本运行时模块寻找的路径。

Events#

Stability: 2 - Stable

events模块提供了监听手机通知、按键、触摸的接口。您可以用他配合自动操作函数完成自动化工作。

events本身是一个EventEmiiter, 但内置了一些事件、包括按键事件、通知事件、Toast事件等。

需要注意的是,事件的处理是单线程的,并且仍然在原线程执行,如果脚本主体或者其他事件处理中有耗时操作、轮询等,则事件将无法得到及时处理(会进入事件队列等待脚本主体或其他事件处理完成才执行)。例如:

auto();
events.observeNotification();
events.on('toast', function(t){
    //这段代码将得不到执行
    log(t);
});
while(true){
    //死循环
}

events.emitter()#

返回一个新的EventEmitter。这个EventEmitter没有内置任何事件。

events.observeKey()#

启用按键监听,例如音量键、Home键。按键监听使用无障碍服务实现,如果无障碍服务未启用会抛出异常并提示开启。

只有这个函数成功执行后, onKeyDown, onKeyUp等按键事件的监听才有效。

该函数在安卓4.3以上才能使用。

events.onKeyDown(keyName, listener)#

注册一个按键监听函数,当有keyName对应的按键被按下会调用该函数。可用的按键名称参见Keys

例如:

//启用按键监听
events.observeKey();
//监听音量上键按下
events.onKeyDown("volume_up", function(event){
    toast("音量上键被按下了");
});
//监听菜单键按下
events.onKeyDown("menu", function(event){
    toast("菜单键被按下了");
    exit();
});

events.onKeyUp(keyName, listener)#

注册一个按键监听函数,当有keyName对应的按键弹起会调用该函数。可用的按键名称参见Keys

一次完整的按键动作包括了按键按下和弹起。按下事件会在手指按下一个按键的"瞬间"触发, 弹起事件则在手指放开这个按键时触发。

例如:

//启用按键监听
events.observeKey();
//监听音量下键弹起
events.onKeyDown("volume_down", function(event){
    toast("音量上键弹起");
});
//监听Home键弹起
events.onKeyDown("home", function(event){
    toast("Home键弹起");
    exit();
});

events.onceKeyDown(keyName, listener)#

注册一个按键监听函数,当有keyName对应的按键被按下时会调用该函数,之后会注销该按键监听器。

也就是listener只有在onceKeyDown调用后的第一次按键事件被调用一次。

events.onceKeyUp(keyName, listener)#

注册一个按键监听函数,当有keyName对应的按键弹起时会调用该函数,之后会注销该按键监听器。

也就是listener只有在onceKeyUp调用后的第一次按键事件被调用一次。

events.removeAllKeyDownListeners(keyName)#

删除该按键的KeyDown(按下)事件的所有监听。

events.removeAllKeyUpListeners(keyName)#

删除该按键的KeyUp(弹起)事件的所有监听。

events.observeTouch()#

启用屏幕触摸监听。(需要root权限)

只有这个函数被成功执行后, 触摸事件的监听才有效。

没有root权限调用该函数则什么也不会发生。

events.setTouchEventTimeout(timeout)#

  • timeout <number> 两个触摸事件的最小间隔。单位毫秒。默认为10毫秒。如果number小于0,视为0处理。

设置两个触摸事件分发的最小时间间隔。

例如间隔为10毫秒的话,前一个触摸事件发生并被注册的监听器处理后,至少要过10毫秒才能分发和处理下一个触摸事件,这10毫秒之间的触摸将会被忽略。

建议在满足需要的情况下尽量提高这个间隔。一个简单滑动动作可能会连续触发上百个触摸事件,如果timeout设置过低可能造成事件拥堵。强烈建议不要设置timeout为0。

events.getTouchEventTimeout()#

返回触摸事件的最小时间间隔。

events.onTouch(listener)#

注册一个触摸监听函数。相当于on("touch", listener)

例如:

//启用触摸监听
events.observeTouch();
//注册触摸监听器
events.onTouch(function(p){
    //触摸事件发生时, 打印出触摸的点的坐标
    log(p.x + ", " + p.y);
});

events.removeAllTouchListeners()#

删除所有事件监听函数。

事件: 'key'#

  • keyCode <number> 键值
  • event <KeyEvent> 事件

当有按键被按下或弹起时会触发该事件。 例如:

auto();
events.observeKey();
events.on("key", function(keyCode, event){
    //处理按键事件
});

其中监听器的参数KeyCode包括:

  • keys.home 主页键
  • keys.back 返回键
  • keys.menu 菜单键
  • keys.volume_up 音量上键
  • keys.volume_down 音量下键

例如:

auto();
events.observeKey();
events.on("key", function(keyCode, event){
    if(keyCode == keys.menu && event.getAction() == event.ACTION_UP){
        toast("菜单键按下");
    }
});

事件: 'key_down'#

  • keyCode <number> 键值
  • event <KeyEvent> 事件

当有按键被按下时会触发该事件。

auto();
events.observeKey();
events.on("key_down", function(keyCode, event){
    //处理按键按下事件
});

事件: 'key_up'#

  • keyCode <number> 键值
  • event <KeyEvent> 事件

当有按键弹起时会触发该事件。

auto();
events.observeKey();
events.on("key_up", function(keyCode, event){
    //处理按键弹起事件
});

事件: 'exit`#

当脚本正常或者异常退出时会触发该事件。事件处理中如果有异常抛出,则立即中止exit事件的处理(即使exit事件有多个处理函数)并在控制台和日志中打印该异常。

一个脚本停止运行时,会关闭该脚本的所有悬浮窗,触发exit事件,之后再回收资源。如果exit事件的处理中有死循环,则后续资源无法得到及时回收。 此时脚本会停留在任务列表,如果在任务列表中关闭,则会强制结束exit事件的处理并回收后续资源。

log("开始运行")
events.on("exit", function(){
    log("结束运行");
});
log("即将结束运行");

events.observeNotification()#

开启通知监听。例如QQ消息、微信消息、推送等通知。

通知监听依赖于通知服务,如果通知服务没有运行,会抛出异常并跳转到通知权限开启界面。(有时即使通知权限已经开启通知服务也没有运行,这时需要关闭权限再重新开启一次)

例如:

events.obverseNotification();
events.onNotification(function(notification){
    log(notification.getText());
});

events.observeToast()#

开启Toast监听。

Toast监听依赖于无障碍服务,因此此函数会确保无障碍服务运行。

事件: 'toast'#

  • toast <Object>
    • getText() 获取Toast的文本内容
    • getPackageName() 获取发出Toast的应用包名

当有应用发出toast(气泡消息)时会触发该事件。但Auto.js软件本身的toast除外。

例如,要记录发出所有toast的应用:

events.observeToast();
events.onToast(function(toast){
    log("Toast内容: " + toast.getText() + " 包名: " + toast.getPackageName());
});

事件: 'notification'#

当有应用发出通知时会触发该事件,参数为Notification

例如:

events.observeNotification();
events.on("notification", function(n){
    log("收到新通知:\n 标题: %s, 内容: %s, \n包名: %s", n.getTitle(), n.getText(), n.getPackageName());
});

Notification#

通知对象,可以获取通知详情,包括通知标题、内容、发出通知的包名、时间等,也可以对通知进行操作,比如点击、删除。

Notification.number#

通知数量。例如QQ连续收到两条消息时number为2。

Notification.when#

通知发出时间的时间戳,可以用于构造Date对象。例如:

events.observeNotification();
events.on("notification", function(n){
    log("通知时间为}" + new Date(n.when));
});

Notification.getPackageName()#

获取发出通知的应用包名。

Notification.getTitle()#

获取通知的标题。

Notification.getText()#

获取通知的内容。

Notification.click()#

点击该通知。例如对于一条QQ消息,点击会进入具体的聊天界面。

Notification.delete()#

删除该通知。该通知将从通知栏中消失。

KeyEvent#

Stability: 2 - Stable

KeyEvent.getAction()#

返回事件的动作。包括:

  • KeyEvent.ACTION_DOWN 按下事件
  • KeyEvent.ACTION_UP 弹起事件

KeyEvent.getKeyCode()#

返回按键的键值。包括:

  • KeyEvent.KEYCODE_HOME 主页键
  • KeyEvent.KEYCODE_BACK 返回键
  • KeyEvent.KEYCODE_MENU 菜单键
  • KeyEvent.KEYCODE_VOLUME_UP 音量上键
  • KeyEvent.KEYCODE_VOLUME_DOWN 音量下键

KeyEvent.getEventTime()#

返回事件发生的时间戳。

KeyEvent.getDownTime()#

返回最近一次按下事件的时间戳。如果本身是按下事件,则与getEventTime()相同。

KeyEvent.keyCodeToString(keyCode)#

把键值转换为字符串。例如KEYCODE_HOME转换为"KEYCODE_HOME"。

keys#

Stability: 2 - Stable

按键事件中所有可用的按键名称为:

  • volume_up 音量上键
  • volume_down 音量下键
  • home 主屏幕键
  • back 返回键
  • menu 菜单键

EventEmitter#

Stability: 2 - Stable

EventEmitter.defaultMaxListeners#

每个事件默认可以注册最多 10 个监听器。 单个 EventEmitter 实例的限制可以使用 emitter.setMaxListeners(n) 方法改变。 所有 EventEmitter 实例的默认值可以使用 EventEmitter.defaultMaxListeners 属性改变。

设置 EventEmitter.defaultMaxListeners 要谨慎,因为会影响所有 EventEmitter 实例,包括之前创建的。 因而,调用 emitter.setMaxListeners(n) 优先于 EventEmitter.defaultMaxListeners。

注意,与Node.js不同,这是一个硬性限制。 EventEmitter 实例不允许添加更多的监听器,监听器超过最大数量时会抛出TooManyListenersException。

emitter.setMaxListeners(emitter.getMaxListeners() + 1);
emitter.once('event', () => {
  // 做些操作
  emitter.setMaxListeners(Math.max(emitter.getMaxListeners() - 1, 0));
});

EventEmitter.addListener(eventName, listener)#

emitter.on(eventName, listener) 的别名。

EventEmitter.emit(eventName[, ...args])#

  • eventName <any>
  • args <any>

按监听器的注册顺序,同步地调用每个注册到名为 eventName 事件的监听器,并传入提供的参数。

如果事件有监听器,则返回 true ,否则返回 false。

EventEmitter.eventNames()#

返回一个列出触发器已注册监听器的事件的数组。 数组中的值为字符串或符号。

const myEE = events.emitter();
myEE.on('foo', () => {});
myEE.on('bar', () => {});

const sym = Symbol('symbol');
myEE.on(sym, () => {});

console.log(myEE.eventNames());
// 打印: [ 'foo', 'bar', Symbol(symbol) ]

EventEmitter.getMaxListeners()#

返回 EventEmitter 当前的最大监听器限制值,该值可以通过 emitter.setMaxListeners(n) 设置或默认为 EventEmitter.defaultMaxListeners。

EventEmitter.listenerCount(eventName)#

  • eventName <string> 正在被监听的事件名

返回正在监听名为 eventName 的事件的监听器的数量。

EventEmitter.listeners(eventName)#

返回名为 eventName 的事件的监听器数组的副本。

server.on('connection', (stream) => {
  console.log('someone connected!');
});
console.log(util.inspect(server.listeners('connection')));
// 打印: [ [Function] ]

EventEmitter.on(eventName, listener)#

  • eventName <any> 事件名
  • listener <Function> 回调函数

添加 listener 函数到名为 eventName 的事件的监听器数组的末尾。 不会检查 listener 是否已被添加。 多次调用并传入相同的 eventName 和 listener 会导致 listener 被添加与调用多次。

server.on('connection', (stream) => {
  console.log('有连接!');
});

返回一个 EventEmitter 引用,可以链式调用。

默认情况下,事件监听器会按照添加的顺序依次调用。 emitter.prependListener() 方法可用于将事件监听器添加到监听器数组的开头。

const myEE = events.emitter();
myEE.on('foo', () => console.log('a'));
myEE.prependListener('foo', () => console.log('b'));
myEE.emit('foo');
// 打印:
//   b
//   a

EventEmitter.once(eventName, listener)#

  • eventName <any> 事件名
  • listener <Function> 回调函数

添加一个单次 listener 函数到名为 eventName 的事件。 下次触发 eventName 事件时,监听器会被移除,然后调用。

server.once('connection', (stream) => {
  console.log('首次调用!');
});

返回一个 EventEmitter 引用,可以链式调用。

默认情况下,事件监听器会按照添加的顺序依次调用。 emitter.prependOnceListener() 方法可用于将事件监听器添加到监听器数组的开头。

const myEE = events.emitter();
myEE.once('foo', () => console.log('a'));
myEE.prependOnceListener('foo', () => console.log('b'));
myEE.emit('foo');
// 打印:
//   b
//   a

EventEmitter.prependListener(eventName, listener)#

  • eventName <any> 事件名
  • listener <Function> 回调函数

添加 listener 函数到名为 eventName 的事件的监听器数组的开头。 不会检查 listener 是否已被添加。 多次调用并传入相同的 eventName 和 listener 会导致 listener 被添加与调用多次。

server.prependListener('connection', (stream) => {
  console.log('有连接!');
});

返回一个 EventEmitter 引用,可以链式调用。

EventEmitter.prependOnceListener(eventName, listener)#

  • eventName <any> 事件名
  • listener <Function> 回调函数

添加一个单次 listener 函数到名为 eventName 的事件的监听器数组的开头。 下次触发 eventName 事件时,监听器会被移除,然后调用。

server.prependOnceListener('connection', (stream) => {
  console.log('首次调用!');
});

返回一个 EventEmitter 引用,可以链式调用。

EventEmitter.removeAllListeners([eventName])#

  • eventName <any>

移除全部或指定 eventName 的监听器。

注意,在代码中移除其他地方添加的监听器是一个不好的做法,尤其是当 EventEmitter 实例是其他组件或模块创建的。

返回一个 EventEmitter 引用,可以链式调用。

EventEmitter.removeListener(eventName, listener)#

从名为 eventName 的事件的监听器数组中移除指定的 listener。

const callback = (stream) => {
  console.log('有连接!');
};
server.on('connection', callback);
// ...
server.removeListener('connection', callback);

removeListener 最多只会从监听器数组里移除一个监听器实例。 如果任何单一的监听器被多次添加到指定 eventName 的监听器数组中,则必须多次调用 removeListener 才能移除每个实例。

注意,一旦一个事件被触发,所有绑定到它的监听器都会按顺序依次触发。 这意味着,在事件触发后、最后一个监听器完成执行前,任何 removeListener() 或 removeAllListeners() 调用都不会从 emit() 中移除它们。 随后的事件会像预期的那样发生。

const myEmitter = events.emitter();

const callbackA = () => {
  console.log('A');
  myEmitter.removeListener('event', callbackB);
};

const callbackB = () => {
  console.log('B');
};

myEmitter.on('event', callbackA);

myEmitter.on('event', callbackB);

// callbackA 移除了监听器 callbackB,但它依然会被调用。
// 触发是内部的监听器数组为 [callbackA, callbackB]
myEmitter.emit('event');
// 打印:
//   A
//   B

// callbackB 被移除了。
// 内部监听器数组为 [callbackA]
myEmitter.emit('event');
// 打印:
//   A

因为监听器是使用内部数组进行管理的,所以调用它会改变在监听器被移除后注册的任何监听器的位置索引。 虽然这不会影响监听器的调用顺序,但意味着由 emitter.listeners() 方法返回的监听器数组副本需要被重新创建。

返回一个 EventEmitter 引用,可以链式调用。

EventEmitter.setMaxListeners(n)#

默认情况下,如果为特定事件添加了超过 10 个监听器,则 EventEmitter 会打印一个警告。 此限制有助于寻找内存泄露。 但是,并不是所有的事件都要被限为 10 个。 emitter.setMaxListeners() 方法允许修改指定的 EventEmitter 实例的限制。 值设为 Infinity(或 0)表明不限制监听器的数量。

返回一个 EventEmitter 引用,可以链式调用。

Floaty#

floaty模块提供了悬浮窗的相关函数,可以在屏幕上显示自定义悬浮窗,控制悬浮窗大小、位置等。

悬浮窗在脚本停止运行时会自动关闭,因此,要保持悬浮窗不被关闭,可以用一个空的setInterval来实现,例如:

setInterval(()=>{}, 1000);

floaty.window(layout)#

  • layout <xml> | <View> 悬浮窗界面的XML或者View

指定悬浮窗的布局,创建并显示一个悬浮窗,返回一个FloatyWindow对象。

其中layout参数可以是xml布局或者一个View,更多信息参见ui模块的说明。

例子:

var w = floaty.window(
    <frame gravity="center">
        <text id="text">悬浮文字</text>
    </frame>
);
setTimeout(()=>{
    w.close();
}, 2000);

这段代码运行后将会在屏幕上显示悬浮文字,并在两秒后消失。

另外,因为脚本运行的线程不是UI线程,而所有对控件的修改操作需要在UI线程执行,此时需要用ui.run,例如:

ui.run(function(){
    w.text.setText("文本");
});

floaty.expandableWindow(collapsedLayout, expandedLayout)#

  • collapsedLayout <xml> | <View> 悬浮窗折叠时的界面XML或者View
  • expandedLayout <xml> | <View> 悬浮窗展开时的界面的XML或者View

指定悬浮窗的布局,创建并显示一个可展开悬浮窗,返回一个ExpandableFloatyWindow对象。

所谓可展开的悬浮窗,以Auto.js的控制台悬浮窗为例,点击右上角的最小化即为悬浮窗折叠状态,再点击悬浮窗则为展开状态。

其中layout参数可以是xml布局或者一个View,更多信息参见ui模块的说明。

例子:

var w = floaty.expandableWindow(
    <img id="logo" src="file:///sdcard/logo.png" w="100" h="100" circle="true"/>
    ,
    <vertical>
        <radiogroup bg="#ffffff">
            <radio text="选项1">
            <radio text="选项2">
            <radio text="选项3">
        </radiogroup>
        <button id="minimize" text="折叠"/>
        <button id="exit" text="关闭悬浮窗"/>
    </vertical>

);


w.logo.click(()=> w.expand());
w.minimize.click(()=> w.collapse());
w.exit.click(()=> w.close());

这个例子运行后将会显示一个图片的图标,点击后显示三个选项和折叠、关闭按键。

floaty.closeAll()#

关闭所有本脚本的悬浮窗。

FloatyWindow#

悬浮窗对象,可通过FloatyWindow.{id}获取悬浮窗界面上的元素。例如, 悬浮窗window上一个控件的id为aaa, 那么window.aaa即可获取到该控件,类似于ui。

FloatyWindow.setAdjustEnabled(enabled)#

  • enabled <boolean> 是否启用悬浮窗调整(大小、位置)

如果enabled为true,则在悬浮窗左上角、右上角显示可供位置、大小调整的标示,就像控制台一样; 如果enabled为false,则隐藏上述标示。

FloatyWindow.setPosition(x, y)#

设置悬浮窗位置。

FloatyWindow.getX()#

返回悬浮窗位置的X坐标。

FloatyWindow.getY()#

返回悬浮窗位置的Y坐标。

FloatyWindow.setSize(width, height)#

设置悬浮窗宽高。

FloatyWindow.getWidht()#

返回悬浮窗宽度。

FloatyWindow.getHeight()#

返回悬浮窗高度。

FloatyWindow.close()#

关闭悬浮窗。如果悬浮窗已经是关闭状态,则此函数将不执行任何操作。

被关闭后的悬浮窗不能再显示。

ExpandableFloatyWindow#

可展开悬浮窗。ExpandableFloatyWindow拥有FloatyWindow的所有函数,同时还有以下函数。

ExpandableFloatyWindow.expand();#

展开悬浮窗。

ExpandableFloatyWindow.collapse();#

折叠悬浮窗。

ExpandableFloatyWindow.toggle();#

如果悬浮窗是折叠状态,则展开悬浮窗;如果是展开状态,则折叠悬浮窗。

ExpandableFloatyWindow.isExpanded();#

返回悬浮窗是否是展开状态。

Files#

Stability: 2 - Stable

files模块提供了一些常见的文件处理,包括文件读写、移动、复制、删掉等。

一次性的文件读写可以直接使用files.read(), files.write(), files.append()等方便的函数,但如果需要频繁读写或随机读写,则使用open()函数打开一个文件对象来操作文件,并在操作完毕后调用close()函数关闭文件。

files.isFile(path)#

返回路径path是否是文件。

log(files.isDir("/sdcard/文件夹/")); //返回false
log(files.isDir("/sdcard/文件.txt")); //返回true

files.isDir(path)#

返回路径path是否是文件夹。

log(files.isDir("/sdcard/文件夹/")); //返回true
log(files.isDir("/sdcard/文件.txt")); //返回false

files.isEmptyDir(path)#

返回文件夹path是否为空文件夹。如果该路径并非文件夹,则直接返回false

files.join(parent, child)#

连接两个路径并返回,例如files.join("/sdcard/", "1.txt")返回"/sdcard/1.txt"。

files.create(path)#

创建一个文件或文件夹并返回是否创建成功。如果文件已经存在,则直接返回false

files.create("/sdcard/新文件夹/");

files.createWithDirs(path)#

创建一个文件或文件夹并返回是否创建成功。如果文件所在文件夹不存在,则先创建他所在的一系列文件夹。如果文件已经存在,则直接返回false

files.createWithDirs("/sdcard/新文件夹/新文件夹/新文件夹/1.txt");

files.exists(path)#

返回在路径path处的文件是否存在。

files.ensureDir(path)#

确保路径path所在的文件夹存在。如果该路径所在文件夹不存在,则创建该文件夹。

例如对于路径"/sdcard/Download/ABC/1.txt",如果/Download/文件夹不存在,则会先创建Download,再创建ABC文件夹。

files.read(path[, encoding = "utf-8"])#

读取文本文件path的所有内容并返回。如果文件不存在,则抛出FileNotFoundException

log(files.read("/sdcard/1.txt"));

files.readBytes(path)#

读取文件path的所有内容并返回一个字节数组。如果文件不存在,则抛出FileNotFoundException

注意,该数组是Java的数组,不具有JavaScript数组的forEach, slice等函数。

一个以16进制形式打印文件的例子如下:

var data = files.readBytes("/sdcard/1.png");
var sb = new java.lang.StringBuilder();
for(var i = 0; i < data.length; i++){
    sb.append(data[i].toString(16));
}
log(sb.toString());

files.write(path, text[, encoding = "utf-8"])#

把text写入到文件path中。如果文件存在则覆盖,不存在则创建。

var text = "文件内容";
//写入文件
files.write("/sdcard/1.txt", text);
//用其他应用查看文件
app.viewFile("/sdcard/1.txt");

files.writeBytes(path, bytes)#

  • path <string> 路径
  • bytes <byte[]> 字节数组,要写入的二进制数据

把bytes写入到文件path中。如果文件存在则覆盖,不存在则创建。

files.append(path, text[, encoding = 'utf-8'])#

把text追加到文件path的末尾。如果文件不存在则创建。

var text = "追加的文件内容";
files.append("/sdcard/1.txt", text);
files.append("/sdcard/1.txt", text);
//用其他应用查看文件
app.viewFile("/sdcard/1.txt");

files.appendBytes(path, text[, encoding = 'utf-8'])#

  • path <string> 路径
  • bytes <byte[]> 字节数组,要写入的二进制数据

把bytes追加到文件path的末尾。如果文件不存在则创建。

files.copy(fromPath, toPath)#

复制文件,返回是否复制成功。例如files.copy("/sdcard/1.txt", "/sdcard/Download/1.txt")

files.move(fromPath, toPath)#

移动文件,返回是否移动成功。例如files.move("/sdcard/1.txt", "/sdcard/Download/1.txt")会把1.txt文件从sd卡根目录移动到Download文件夹。

files.rename(path, newName)#

重命名文件,并返回是否重命名成功。例如files.rename("/sdcard/1.txt", "2.txt")

files.renameWithoutExtension(path, newName)#

重命名文件,不包含拓展名,并返回是否重命名成功。例如files.rename("/sdcard/1.txt", "2")会把"1.txt"重命名为"2.txt"。

files.getName(path)#

返回文件的文件名。例如files.getName("/sdcard/1.txt")返回"1.txt"。

files.getNameWithoutExtension(path)#

返回不含拓展名的文件的文件名。例如files.getName("/sdcard/1.txt")返回"1"。

files.getExtension(path)#

返回文件的拓展名。例如files.getExtension("/sdcard/1.txt")返回"txt"。

files.remove(path)#

删除文件或空文件夹,返回是否删除成功。

files.removeDir(path)#

删除文件夹,如果文件夹不为空,则删除该文件夹的所有内容再删除该文件夹,返回是否全部删除成功。

files.getSdcardPath()#

返回SD卡路径。所谓SD卡,即外部存储器。

files.cwd()#

返回脚本的"当前工作文件夹路径"。该路径指的是,如果脚本本身为脚本文件,则返回这个脚本文件所在目录;否则返回null获取其他设定路径。

例如,对于脚本文件"/sdcard/脚本/1.js"运行files.cwd()返回"/sdcard/脚本/"。

files.listDir(path[, filter])#

  • path <string> 路径
  • filter <Function> 过滤函数,可选。接收一个string参数(文件名),返回一个boolean值。

列出文件夹path下的满足条件的文件和文件夹的名称的数组。如果不加filter参数,则返回所有文件和文件夹。

列出sdcard目录下所有文件和文件夹为:

var arr = files.listDir("/sdcard/");
log(arr);

列出脚本目录下所有js脚本文件为:

var dir = "/sdcard/脚本/";
var jsFiles = files.listDir(dir, function(name){
    return name.endsWith(".js") && files.isFile(files.join(dir, name));
});
log(jsFiles);

open(path[, mode = "r", encoding = "utf-8", bufferSize = 8192])#

  • path <string> 文件路径,例如"/sdcard/1.txt"。
  • mode <string> 文件打开模式,包括:
    • "r": 只读文本模式。该模式下只能对文件执行文本读取操作。
    • "w": 只写文本模式。该模式下只能对文件执行文本覆盖写入操作。
    • "a": 附加文本模式。该模式下将会把写入的文本附加到文件末尾。
    • "rw": 随机读写文本模式。该模式下将会把写入的文本附加到文件末尾。
      目前暂不支持二进制模式,随机读写模式。
  • encoding <string> 字符编码。
  • bufferSize <number> 文件读写的缓冲区大小。

打开一个文件。根据打开模式返回不同的文件对象。包括:

  • "r": 返回一个ReadableTextFile对象。
  • "w", "a": 返回一个WritableTextFile对象。

对于"w"模式,如果文件并不存在,则会创建一个,已存在则会清空该文件内容;其他模式文件不存在会抛出FileNotFoundException。

ReadableTextFile#

可读文件对象。

ReadableTextFile.read()#

返回该文件剩余的所有内容的字符串。

ReadableTextFile.read(maxCount)#

  • maxCount <Number> 最大读取的字符数量

读取该文件接下来最长为maxCount的字符串并返回。即使文件剩余内容不足maxCount也不会出错。

ReadableTextFile.readline()#

读取一行并返回(不包含换行符)。

ReadableTextFile.readlines()#

读取剩余的所有行,并返回它们按顺序组成的字符串数组。

close()#

关闭该文件。

打开一个文件不再使用时务必关闭

PWritableTextFile#

可写文件对象。

PWritableTextFile.write(text)#

把文本内容text写入到文件中。

PWritableTextFile.writeline(line)#

把文本line写入到文件中并写入一个换行符。

PWritableTextFile.writelines(lines)#

把很多行写入到文件中....

PWritableTextFile.flush()#

把缓冲区内容输出到文件中。

PWritableTextFile.close()#

关闭文件。同时会被缓冲区内容输出到文件。

打开一个文件写入后,不再使用时务必关闭,否则文件可能会丢失

全局变量与函数#

全局变量和函数在所有模块中均可使用。 但以下变量的作用域只在模块内,详见 module

  • exports
  • module
  • require() 以下的对象是特定于 Auto.js 的。 有些内置对象是 JavaScript 语言本身的一部分,它们也是全局的。

一些模块中的函数为了使用方便也可以直接全局使用,这些函数在此不再赘述。例如timers模块的setInterval, setTimeout等函数。

sleep(n)#

暂停运行n毫秒的时间。1秒等于1000毫秒。

//暂停5毫秒
sleep(5000);

currentPackage()#

返回最近一次监测到的正在运行的应用的包名,一般可以认为就是当前正在运行的应用的包名。

此函数依赖于无障碍服务,如果服务未启动,则抛出异常并提示用户启动。

currentActivity()#

返回最近一次监测到的正在运行的Activity的名称,一般可以认为就是当前正在运行的Activity的名称。

此函数依赖于无障碍服务,如果服务未启动,则抛出异常并提示用户启动。

setClip(text)#

设置剪贴板内容。此剪贴板即系统剪贴板,在一般应用的输入框中"粘贴"既可使用。

setClip("剪贴板文本");

getClip()#

返回系统剪贴板的内容。

toast("剪贴板内容为:" + getClip());

toast(message)#

以气泡显示信息message几秒。(具体时间取决于安卓系统,一般都是2秒)

注意,信息的显示是"异步"执行的,并且,不会等待信息消失程序才继续执行。如果在循环中执行该命令,可能出现脚本停止运行后仍然有不断的气泡信息出现的情况。 例如:

for(var i = 0; i < 100; i++){
  toast(i);
}

运行这段程序以后,会很快执行完成,且不断弹出消息,在任务管理中关闭所有脚本也无法停止。 要保证气泡消息才继续执行可以用:

for(var i = 0; i < 100; i++){
  toast(i);
  sleep(2000);
}

或者修改toast函数:

var _toast_ = toast;
toast = function(message){
  _toast_(message);
  sleep(2000);
}
for(var i = 0; i < 100; i++){
  toast(i);
}

toastLog(message)#

相当于toast(message);log(message)。显示信息message并在控制台中输出。参见console.log。

waitForActivity(activity[, period = 200])#

  • activity Activity名称
  • period 轮询等待间隔(毫秒)

等待指定的Activity出现,period为检查Activity的间隔。

waitForPackage(package[, period = 200])#

  • package 包名
  • period 轮询等待间隔(毫秒)

等待指定的应用出现。例如waitForPackage("com.tencent.mm")为等待当前界面为微信。

exit()#

立即停止脚本运行。

立即停止是通过抛出ScriptInterrupttedException来实现的,因此如果用try...catch把exit()函数的异常捕捉,则脚本不会立即停止,仍会运行几行后再停止。

random(min, max)#

返回一个在[min...max]之间的随机数。例如random(0, 2)可能产生0, 1, 2。

random()#

返回在[0, 1)的随机浮点数。

context#

全局变量。一个android.content.Context对象。

注意该对象为ApplicationContext,因此不能用于界面、对话框等的创建。

HTTP#

Stability: 2 - Stable

http模块提供一些进行http请求的函数。

http.get(url[, options, callback])#

对地址url进行一次HTTP GET 请求。如果没有回调函数,则在请求完成或失败时返回此次请求的响应(参见[Response][])。

最简单GET请求如下:

console.show();
var r = http.get("www.baidu.com");
log("code = " + r.statusCode);
log("html = " + r.body.string());

采用回调形式的GET请求如下:

console.show();
http.get("www.baidu.com", {}, function(res, err){
    if(err){
        console.error(err);
        return;
    }
    log("code = " + r.statusCode);
    log("html = " + r.body.string());
});

如果要增加HTTP头部信息,则在options参数中添加,例如:

console.show();
var r = http.get("www.baidu.com", {
    headers: {
        "Accept-Language": "zh-cn,zh;q=0.5",
        "User-Agent: "Mozilla/5.0 (Macintosh; Intel Mac OS X
10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56
Safari/535.11"
    }
});
log("code = " + r.statusCode);
log("html = " + r.body.string());

一个请求天气并解析返回的天气JSON结果的例子如下:

var city = "广州";
var res = http.get("http://www.sojson.com/open/api/weather/json.shtml?city=" + city);
if(res.statusCode != 200){
    toast("请求失败: " + res.statusCode + " " + res.statusMessage);
}else{
    var weather = res.body.json();
    log(weather);
    toast(util.format("温度: %s 湿度: %s 空气质量: %s", weather.data.wendu,
        weather.data.shidu, weather.quality));
}

http.post(url, data[, options, callback])#

对地址url进行一次HTTP POST 请求。如果没有回调函数,则在请求完成或失败时返回此次请求的响应(参见[Response][])。

其中POST数据可以是字符串或键值对。具体含义取决于options.contentType的值。默认为"application/x-www-form-urlencoded"(表单提交), 这种方式是JQuery的ajax函数的默认方式。

一个模拟表单提交登录淘宝的例子如下:

var url = "https://login.taobao.com/member/login.jhtml";
var username = "你的用户名";
var password = "你的密码";
var res = http.post(url, {
    "TPL_username": username,
    "TPL_password": password
});
var html = res.body.string();
if(html.contains("页面跳转中")){
    toast("登录成功");
}else{
    toast("登录失败");
}

http.postJson(url[, data, options, callback])#

以JSON格式向目标Url发起POST请求。如果没有回调函数,则在请求完成或失败时返回此次请求的响应(参见[Response][])。

JSON格式指的是,将会调用JSON.stringify()把data对象转换为JSON字符串,并在HTTP头部信息中把"Content-Type"属性置为"application/json"。这种方式是AngularJS的ajax函数的默认方式。

一个调用图灵机器人接口的例子如下:

http.postMultipart(url, files[, options, callback])#

向目标地址发起类型为multipart/form-data的请求(通常用于文件上传等), 其中files参数是<name1: value1, name2: value2, ...>的键值对,value的格式可以是以下几种情况:

  1. string
  2. 文件类型,即open()返回的类型
  3. [fileName, filePath]
  4. [fileName, mimeType, filePath]

其中1属于非文件参数,2、3、4为文件参数。举个例子,最简单的文件上传的请求为:

var res = http.postMultipart(url, {
    file: open("/sdcard/1.txt")
});
log(res.body.string());

如果使用格式2,则代码为

var res = http.postMultipart(url, {
    file: ["1.txt", "/sdcard/1.txt"]
});
log(res.body.string());

如果使用格式3,则代码为

var res = http.postMultipart(url, {
    file: ["1.txt", "text/plain", "/sdcard/1.txt"]
});
log(res.body.string());

如果使用格式2的同时要附带非文件参数"appId=abcdefghijk",则为:

var res = http.postMultipart(url, {
    appId: "adcdefghijk",
    file: open("/sdcard/1.txt")
});
log(res.body.string());

http.request(url[, options, callback])#

对目标地址url发起一次HTTP请求。如果没有回调函数,则在请求完成或失败时返回此次请求的响应(参见[Response][])。

选项options可以包含以下属性:

该函数是get, post, postJson等函数的基础函数。因此除非是PUT, DELET等请求,或者需要更高定制的HTTP请求,否则直接使用get, post, postJson等函数会更加方便。

Response#

HTTP请求的响应。

Response.statusCode#

当前响应的HTTP状态码。例如200(OK), 404(Not Found)等。

有关HTTP状态码的信息,参见菜鸟教程:HTTP状态码

Response.statusMessage#

当前响应的HTTP状态信息。例如"OK", "Bad Request", "Forbidden"。

有关HTTP状态码的信息,参见菜鸟教程:HTTP状态码

例子:

var res = http.get("www.baidu.com");
if(res.statusCode >= 200 && res.statusCode < 300){
    toast("页面获取成功!");
}else if(res.statusCode == 404){
    toast("页面没找到哦...");
}else{
    toast("错误: " + res.statusCode + " " + res.statusMessage);
}

Response.headers#

当前响应的HTTP头部信息。该对象的键是响应头名称,值是各自的响应头值。 所有响应头名称都是小写的(吗)。

有关HTTP头部信息,参见菜鸟教程:HTTP响应头信息

例子:

console.show();
var res = http.get("www.qq.com");
console.log("HTTP Headers:")
for(var headerName in res.headers){
    console.log("%s: %s", headerName, res.headers[headerName]);
}

Response.body#

当前响应的内容。他有以下属性和函数:

  • bytes() <Array> 以字节数组形式返回响应内容
  • string() <string> 以字符串形式返回响应内容
  • json() <Object> 把响应内容作为JSON格式的数据并调用JSON.parse,返回解析后的对象
  • contentType <string> 当前响应的内容类型

Response.request#

  • <Request> 当前响应所对应的请求。参见[Request][]。

Response.url#

  • <number> 当前响应所对应的请求URL。

Response.method#

  • <string> 当前响应所对应的HTTP请求的方法。例如"GET", "POST", "PUT"等。

Images#

Stability: 2 - Stable

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

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)#

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

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

images.pixel(image, x, y)#

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

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

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

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

images.save(image, path)#

  • image <image> 图片
  • path <string> 路劲

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

images.read(path)#

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

images.load(url)#

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

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为任何颜色都能匹配)。默认为16。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])#

严格找色的简便方法。找色时要求颜色完全相等才匹配。

相当于

images.findColor(img, color, {
   region: [x, y, width, height],
   threshold: 0
});

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

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

requestScreenCapture();
launchApp("QQ");
sleep(1200);
var p = findColorEquals(captureScreen(), "#f64d30");
if(p){
    toast("有未读消息");
}else{
    toast("没有未读消息");
}

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
})

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

colors#

Stability: 2 - Stable

colors是颜色处理的工具对象。包含一些常用方法,包括android.graphics.Color的所有方法以及toString方法。

colors.toString(color)#

返回颜色值的字符串,格式为 #AARRGGBB。

colors.red(color)#

返回颜色color的R通道的值,范围0~255.

colors.green(color)#

返回颜色color的G通道的值,范围0~255.

colors.blue(color)#

返回颜色color的B通道的值,范围0~255.

colors.alpha(color)#

返回颜色color的Alpha通道的值,范围0~255.

colors.rgb(red, green, blue)#

返回这些颜色通道构成的整数颜色值。Alpha通道将是255(不透明)。

colors.argb(alpha, red, green, blue)#

返回这些颜色通道构成的整数颜色值。

Image#

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

Image.getWidth()#

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

Image.getHeight()#

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

Image.saveTo(path)#

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

Image.pixel(x, y)#

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

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

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

Point#

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

Point.x#

横坐标。

Point.y#

纵坐标。

Keys#

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

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

back()#

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

home()#

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

powerDialog()#

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

notifications()#

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

quickSettings()#

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

recents()#

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

splitScreen()#

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

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

module (模块)#

Stability: 2 - Stable

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

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

const 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 = (r) => PI * r ** 2;

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
  };
};

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+1个可滑动控件上滑或左滑。返回是否操作成功。屏幕上没有可滑动的控件时返回false。

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

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

scrollDown([i])#

找到第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)是稍微复杂的自动操作脚本所必须。使用选择器的一般步骤是:

  1. 用各种筛选条件找出想要的控件,如某个文本控件、列表控件、图片控件
  2. 对找出的控件执行想要的操作,如点击、滑动、设置文字、长按

问题的关键在于如何找出想要的控件。对于文本控件一般通过他的文字来定位他,图片控件则通过id或者图片描述(desc)。选择器所做的就是提供这些筛选条件并给出筛选结果。

选择器提供了更强大的定位界面控件与对其进行操作的功能,可以用他们完成更多的自动操作和提取界面信息。
选择器通过附加筛选条件,筛选出符合条件的控件或控件集合,之后可以对这些控件这些操作。
可供选择的筛选条件包括:

  • id
  • text 文本
  • desc 描述(Content-Description)
  • className 类名
  • packageName 包名
  • bounds 在屏幕上的范围
  • drawingOrder 绘制顺序
  • 各种属性,主要包括:
    • clickable 可点击
    • longClickable 可长按
    • checkable 可勾选
    • checked 被勾选
    • scrollable 可滑动
    • selected 被选择
    • editable 可编辑
    • visibleToUser 可见
    • enabled 已启用

要对选择器所确定的筛选条件,对屏幕上的控件进行筛选,调用方法find(), findOne(), untilFind()。其中find(), untilFind()返回控件的集合,findOne()返回一个控件。untilFind和findOne函数会一直寻找直到屏幕上出现满足条件的控件为止。
一个完整的选择器示例如下:

var emoj = id("name").packageName("com.tencent.mobileqq").clickable()
        .className("ImageView").drawingOrder(5).findOne();
emoj.click();

上述代码是找到QQ聊天界面下面的表情按钮(第5个按钮)并点击。也可以简化为:

id("name").packageName("com.tencent.mobileqq").clickable()
       .className("ImageView").click();

这段代码意思是,找出id为"name", 包名为"com.tentcent.mobileqq",类名为ImageView的所有可点击控件,对他们执行点击动作。

最后的click()函数相当于untilFind().click()。也就是如果只需要在找到控件以后立即进行操作,只需要调用相应的操作函数,而不需要先调用find等函数等到控件再操作。
要获取屏幕上的控件的信息(例如id, text, desc, bounds等),可以开启悬浮窗,使用界面层次查看和界面范围查看。

选择器部分内容如果有安卓开发经验会更容易掌握。

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严格匹配筛选条件。

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

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

idContains(str)#

附加控件id包含字符串str的筛选条件。

idStartsWith(prefix)#

附加id需要以prefix开头的筛选条件。

idEndsWith(suffix)#

附加id需要以suffix结束的筛选条件。

idMatches(String)#

  • String <string> id要满足的正则表达式

附加id需要满足正则表达式。有关正则表达式,可以查看菜鸟教程

这里的正则表达式是Java的正则表达式,不能直接使用JavaScript的正则表达式。

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

text(str)#

附加控件文本等于字符串str的筛选条件。

控件的text(文本)属性是控件上的显示的文字,例如微信左上角的"微信"文本。对于图片或者其他控件通常text属性为null。

textContains(str)#

附加控件文本需要以prefix开头的筛选条件。

textStartsWith(prefix)#

附加控件文本需要以prefix开头的筛选条件。

textEndsWith(suffix)#

附加控件文本需要以suffix结束的筛选条件。

textMatches(str)#

  • String <string> 要满足的正则表达式

附加控件文本需要满足正则表达式的条件。

desc(str)#

附加控件描述等于字符串str的筛选条件。

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

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

descContains(str)#

附加控件描述需要以prefix开头的筛选条件。

descStartsWith(prefix)#

附加控件描述需要以prefix开头的筛选条件。

descEndsWith(suffix)#

附加控件描述需要以suffix结束的筛选条件。

descMatches(str)#

  • String <string> 要满足的正则表达式

附加控件描述需要满足正则表达式的条件。

className(str)#

附加控件类名等于字符串str的筛选条件。

控件的className属性是一个控件的具体类型,例如图片控件通常为ImageView,文本控件通常为TextView, 输入框通常为EditText。(若一个控件是在android.widget包里的,那么android.widget可以省略。但除此之外的其他控件必须是类的全名,例如"com.stardust.theme.ThemeColorImageView")。

classNameContains(str)#

附加控件类名需要以prefix开头的筛选条件。

classNameStartsWith(prefix)#

附加控件类名需要以prefix开头的筛选条件。

classNameEndsWith(suffix)#

附加控件类名需要以suffix结束的筛选条件。

classNameMatches(str)#

  • String <string> 要满足的正则表达式

附加控件类名需要满足正则表达式的条件。

packageName(str)#

附加包名等于字符串str的筛选条件。

packageName属性也即控件所属的应用的包名(参见《shell命令:应用包名》),通常用来保证当前运行的应用是想要的应用。

packageNameContains(str)#

附加包名需要以prefix开头的筛选条件。

packageNameStartsWith(prefix)#

附加包名需要以prefix开头的筛选条件。

packageNameEndsWith(suffix)#

附加包名需要以suffix结束的筛选条件。

packageNameMatches(str)#

  • String <string> 要满足的正则表达式

附加包名需要满足正则表达式的条件。

bounds(l, t, r, b)#

  • l, t, r, b 用来定位控件在屏幕上的范围的四个整数

boundsInside(l, t, r, b)#

boundsContains(l, t, r, b)#

drawingOrder(order)#

  • order <number> 控件在父视图中的绘制顺序,在一些

checkable([b = true])#

  • b <Boolean> 表示控件是否可勾选, 默认为true

附加控件是否可勾选的条件。可勾选指的是,例如QQ和微信发送图片时图片的勾选。

selected([b = true])#

  • b <Boolean> 表示控件是否被选中,默认为true

附加控件是否被选中的条件。被选中指的是,例如QQ聊天界面点击下方的表情按钮时,会出现自己收藏的表情,这时表情按钮便处于选中状态。

clickable([b = true])#

  • b <Boolean> 表示控件是否可点击, 默认为true

附加控件是否可点击的条件。但并非所有clickable为false的控件都真的不能点击,这取决于控件的实现。因而要确定一个控件的clickable属性,可以开启悬浮窗工具。

longClickable([b = true])#

  • b <Boolean> 表示控件是否可长按, 默认为true

附加控件是否可长按的条件。

enabled([b = true])#

  • b <Boolean> 表示控件是否已启用, 默认为true

附加控件是否已启用的条件。大多数控件都是启用的状态,处于“禁用”状态通常是灰色并且不可点击。

scrollable([b = true])#

  • b <Boolean> 表示控件是否可滑动, 默认为true

附加控件是否可滑动的条件。

editable([b = true])#

  • b <Boolean> 表示控件是否可编辑, 默认为true

附加控件是否可编辑的条件。一般来说可编辑的控件为输入框(EditText)。

contentInvalid([b = true])#

  • b <Boolean> 表示控件是否是contentInvalid, 默认为true

我也不知道是什么,下次再补充吧。

contextClickable([b = true])#

  • b <Boolean> 表示控件是否是contextClickable, 默认为true

我也不知道是什么,下次再补充吧。

multiLine([b = true])#

  • b <Boolean> 表示文本或输入框控件是否是多行显示的, 默认为true

附加控件是否文本或输入框控件是否是多行显示的条件。

findOne()#

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

find()#

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

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

var c = className("AbsListView").find();
if(c.empty()){
    toast("找到啦");
}else{
    toast("没找到╭(╯^╰)╮");
}

untilFind()#

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

exists()#

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

if(text("嘿嘿嘿").exists()){
    //嘿嘿嘿
}

waitFor()#

等待屏幕上出现符合条件的控件。

text("嘿嘿嘿").waitFor();

click()#

找到所有符合条件的控件并点击。相当于untilFind().click()。参见click)。

longClick()#

找到所有符合条件的控件并长按。相当于untilFind().longClick()。参见longClick)。

copy()#

找到所有符合条件的控件并复制其内容,只对文本框或输入框控件有文字选中时有效。相当于untilFind().copy()。参见copy)。

paste()#

找到所有符合条件的控件并粘贴,只对输入框控件有效。相当于untilFind().paste()。参见paste)。

select()#

找到所有符合条件的控件并选中。相当于untilFind().select()。参见select)。

cut()#

找到所有符合条件的控件并剪切其内容,只对文本框或输入框控件有文字选中时有效。相当于untilFind().cut()。参见cut)。

collapse()#

找到所有符合条件的控件并折叠。相当于untilFind().collapse()。参见collapse)。

expand()#

找到所有符合条件的控件并展开。相当于untilFind().expand()。参见expand)。

show()#

找到所有符合条件的控件并使其出现在屏幕上。相当于untilFind().show()。参见show)。

scrollForward()#

找到所有符合条件的控件并向前滑。相当于untilFind().scrollForward()。参见scrollForward)。
向前滑指的是,对于左右滑动的控件向右滑,上下滑动的控件向下滑。

scrollBackward()#

找到所有符合条件的控件并向后滑。相当于untilFind().scrollBackward()。参见scrollBackward)。
向后滑指的是,对于左右滑动的控件向左滑,上下滑动的控件向上滑。

scrollUp()#

找到所有符合条件的控件并向上滑。相当于untilFind().scrollUp()。参见scrollUp)。

scrollDown()#

找到所有符合条件的控件并向下滑。相当于untilFind().scrollDown()。参见scrollDown)。

scrollLeft()#

找到所有符合条件的控件并向左滑。相当于untilFind().scrollLeft()。参见scrollLeft)。

scrollRight()#

找到所有符合条件的控件并向右滑。相当于untilFind().scrollRight()。参见scrollRight)。

contextClick()#

文档缺失

setSelection(start, end)#

找到所有符合条件的控件并设置文字选中区域,只对文本控件和输入框有效。相当于untilFind().setSelection()。参见setSelection

setText(text)#

  • text <string> 要设置的文本 找到所有符合条件的控件并设置文字,只对输入框有效。相当于untilFind().setText()。参见setText

UiCollection#

UiCollection, 控件集合, 通过选择器的find(), untilFind()方法返回的对象。可以对其进行操作或者获取其信息。

UiCollection.size()#

返回集合中的控件数。

UiCollection.get(i)#

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

UiCollection.each(func)#

  • func \ 遍历函数

遍历集合。例如:

var c = clickable();
c.each(function(o){
    log(o.text());
});

empty()#

返回控件集合是否为空。

nonEmpty()#

返回控件集合是否非空。

UiCollection.filter(filter)#

  • filter \ 过滤函数。参数为UiObject,返回值为Boolean。

过滤出控件集合中符合条件的子控件。例如要过滤出所有子控件数目为1的控件为:

var newCollection = collection.filter(function(obj){
    return obj.childCount() == 1;
});

UiCollection.find(selector)#

  • selector \ 选择器

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

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

例如:

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

UiCollection.findOne(selector)#

  • selector \ 选择器

根据selector所确定的条件在该控件集合中找到一个符合条件的控件并返回控件。若找不到则返回null。

UiCollection.click()#

点击集合中所有控件,并返回是否全部点击成功。

UiCollection.longClick()#

长按集合中所有控件,并返回是否全部操作成功。

UiCollection.copy()#

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

UiCollection.paste()#

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

UiCollection.select()#

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

UiCollection.cut()#

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

UiCollection.collapse()#

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

UiCollection.expand()#

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

UiCollection.show()#

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

UiCollection.scrollForward()#

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

UiCollection.scrollBackward()#

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

UiCollection.scrollUp()#

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

UiCollection.scrollDown()#

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

UiCollection.scrollLeft()#

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

UiCollection.scrollRight()#

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

UiCollection.contextClick()#

UiCollection.setSelection(start, end)#

对集合中所有控件设置文字选中区域,并返回是否全部操作成功。

UiCollection.setText(text)#

  • text <string> 要设置的文本 对集合中所有控件设置文本,并返回是否全部操作成功。

UiObject#

控件, 选择器的findOne方法返回的对象。和UiCollection有相似的方法,不再赘述。除此之外还有以下方法。

child(i)#

返回第i+1个子控件(UiObject)。

parent()#

返回父控件(UiObject)。

children()#

返回该控件的所有子控件组成的控件集合。

childCount()#

返回子控件数目。

bounds()#

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

boundsInParent()#

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

drawingOrder()#

返回控件的绘制次序。

id()#

返回控件的id,可能为null。

text()#

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

findByText(text)#

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

find(selector)#

  • selector \ 选择器

根据选择器selector在子控件中递归地寻找符合条件的控件,返回它们组成的集合。

findOne#

find(selector)#

  • selector \ 选择器

根据选择器selector在子控件中递归地寻找一个符合条件的控件。找不到则返回null。

Rect#

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

Rect.left#

长方形左边界的x坐标、

Rect.right#

长方形左边界的x坐标、

Rect.top#

长方形上边界的y坐标、

Rect.bottom#

长方形下边界的y坐标、

Rect.centerX()#

长方形中点x坐标。

Rect.centerY()#

长方形中点y坐标。

Rect.width()#

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

Rect.height()#

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

Rect.contains(r)#

  • r \

返回是否包含另一个长方形r。

Rect.intersect(r)#

  • r \

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

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 = false])#

  • 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进程。

Shell(root = false)#

  • root <Boolean> 是否以root权限运行一个shell进程。这将会影响其后使用该Shell对象执行的命令的权限

Shell对象的"构造函数"。

var sh = new Shell(true);
//强制停止微信
sh.exec("am force-stop com.tencent.mm");
sh.exit();

Shell.exec(cmd)#

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

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

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

Shell.exit()#

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

Shell.exitAndWaitFor()#

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

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

Shell.setCallback(callback)#

设置该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, ArrayJSON.stringify序列化存取。

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

storages.create(name)#

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

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

var storage = stroages.create("ABC");
storage.put("a", 123);

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

var storage = storages.create("ABC");
log("a = " + storage.get("a"));

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

var storage = storages.create("2732014414@qq.com:ABC");

storages.remove(name)#

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

Storages#

Storage.get(key[, defaultValue])#

  • key <string> 键值
  • defaultValue <any> 可选,默认值

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

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

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

Storage.put(key, value)#

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

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

Storage.remove(key)#

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

Storage.contains(key)#

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

Storage.clear()#

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

Threads#

Stability: 1 - Experiment

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

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

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

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

threads.start(action)#

启动一个新线程并执行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()#

返回当前线程。

threads.disposable()#

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

threads.atomic([initialValue])#

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

threads.lock()#

新建一个可重入锁。更多信息参见线程安全以及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()#

返回线程是否存活。如果线程仍未开始或已经结束,返回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加上同步锁并作为一个新函数返回。

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 对象。