diff --git a/app/src/main/assets/docs/all.html b/app/src/main/assets/docs/all.html index 16913ed1..e8362645 100644 --- a/app/src/main/assets/docs/all.html +++ b/app/src/main/assets/docs/all.html @@ -72,15 +72,23 @@
app模块提供一系列函数,用于与其他应用的交互。例如打开文件、拍照、发送邮件等。
-同时提供了方便的基础函数startActivity和sendBroadcast,用他们可完成app模块没有内置的和其他应用的交互。
+app模块提供一系列函数,用于使用其他应用、与其他应用交互。例如发送意图、打开文件、发送邮件等。
+同时提供了方便的进阶函数startActivity和sendBroadcast,用他们可完成app模块没有内置的和其他应用的交互。
+appName <string> 应用名称通过应用名称启动应用。如果该名称对应的应用不存在,则返回false; 否则返回true。如果该名称对应多个应用,则只启动其中某一个。
+该函数也可以作为全局函数使用。
+launchApp("Auto.js");
+packageName <string> 应用包名通过应用包名启动应用。如果该包名对应的应用不存在,则返回false;否则返回true。
+该函数也可以作为全局函数使用。
+//启动微信
+launch("com.tencent.mm");
+packageName <string> 应用包名相当于app.launch(packageName)。
appName <string> 应用名称获取应用名称对应的已安装的应用的包名。如果该找不到该应用,返回null;如果该名称对应多个应用,则只返回其中某一个的包名。
+该函数也可以作为全局函数使用。
+var name = getPackageName("QQ"); //返回"com.tencent.mobileqq"
+packageName <string> 应用包名获取应用包名对应的已安装的应用的名称。如果该找不到该应用,返回null。
+该函数也可以作为全局函数使用。
+var name = getAppName("com.tencent.mobileqq"); //返回"QQ"
+packageName <string> 应用包名打开应用的详情页(设置页)。如果找不到该应用,返回false; 否则返回true。
+该函数也可以作为全局函数使用。
用其他应用查看文件。
-用其他应用查看文件。文件不存在的情况由查看文件的应用处理。
+如果找不出可以查看该文件的应用,则抛出ActivityNotException。
//查看文本文件
+app.viewFile("/sdcard/1.txt");
+用其他应用编辑文件。
-用其他应用编辑文件。文件不存在的情况由编辑文件的应用处理。
+如果找不出可以编辑该文件的应用,则抛出ActivityNotException。
//编辑文本文件
+app.editFile("/sdcard/1.txt/);
+卸载应用。
-卸载应用。执行后会会弹出卸载应用的提示框。如果该包名的应用未安装,由应用卸载程序处理,可能弹出"未找到应用"的提示。
+//卸载QQ
+app.uninstall("com.tencent.mobileqq");
+url <string> 网站的Url,如果不以"http://"或"https://"开头则默认是"http://"。用浏览器打开网站url。
-path <string> 照片保存路径调用相机应用拍照,完成后保存到路径path。
+如果没有安装浏览器应用,则抛出ActivityNotException。
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 可以通过多种方式促进组件之间的通信,但其基本用例主要包括以下三个:
+启动活动(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号的聊天窗口等。
+
+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对象构造的android.content.Intent对象。
+根据选项,构造一个意图Intent对象。
例如:
-var i = app.intent({
- action: "android.intent.action.VIEW",
- type: "text/plain",
- data: "file:///sdcard/1.txt",
+//打开应用来查看图片文件
+var i = app.intent({
+ action: "VIEW",
+ type: "image/png",
+ data: "file:///sdcard/1.png"
});
-
如果你看了一脸懵逼,请百度安卓Intent。
-app.startActivity(intent)#
+app.startAcvitity(i);
+更多信息,请百度安卓Intent或参考Android指南: Intent。
+相当于context.startActivity(intent)。
-根据选项构造一个Intent,并启动该Activity。
+相当于context.sendBroadcast(intent)。
+根据选项构造一个Intent,并发送该广播。
详见 util.format()。
+该函数也可以作为全局函数使用。
data 断言。如果value为false则输出错误信息message并停止脚本运行。
-var a = 1 + 1;
+console.assert(a == 2, "加法出错啦");
+data ...args 与console.log一样输出信息,并在控制台显示输入框等待输入。按控制台的确认按钮后会将输入的字符串用eval计算后返回。
-部分机型可能会有控制台不显示输入框的情况,属于bug。
+部分机型可能会有控制台不显示输入框的情况,属于bug。
例如:
var n = console.input("请输入一个数字:");
//输入123之后:
@@ -812,7 +919,7 @@ toast(n + 1);
与console.log一样输出信息,并在控制台显示输入框等待输入。按控制台的确认按钮后会将输入的字符串直接返回。
部分机型可能会有控制台不显示输入框的情况,属于bug。
例如:
-var n = console.input("请输入一个数字:");
+var n = console.rawInput("请输入一个数字:");
//输入123之后:
toast(n + 1);
//显示1231
@@ -822,22 +929,47 @@ toast(n + 1);
h <number> 高度
设置控制台的大小,单位像素。
-console.setPosition(x, y)#
+console.show();
+//设置控制台大小为屏幕的四分之一
+console.setSize(device.width / 2, device.height / 2);
+
console.setPosition(x, y)#
设置控制台的位置,单位像素。
-print(text)#
+console.show();
+console.setPosition(100, 100);
+
print(text)#
在控制台中输出文本text。不会自动换行。
+相当于log(text)。
-Android7.0以上点按与手势模拟#
-Stability: 2 - Stable本章节介绍了一些适用于Android7.0以上、不需要root权限、依赖于无障碍服务的点按与手势模拟的全局函数。
-注意以下命令只有Android7.0及以上才有效
+基于坐标的触摸模拟#
+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)#
+设置脚本坐标点击所适合的屏幕宽高。如果脚本运行时,屏幕宽度不一致会自动放缩坐标。
+例如在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 <number> 要点击的坐标的x值
@@ -845,16 +977,13 @@ toast(n + 1);
模拟点击坐标(x, y),并返回是否点击成功。只有在点击执行完成后脚本才继续执行。
一般而言,只有点击过程(大约150毫秒)中被其他事件中断(例如用户自行点击)才会点击失败。
-使用该函数模拟连续点击时可能有点击速度过慢的问题,这时可以用[press][]函数代替。
-
-可以在开发者选项中启用指针位置来查看坐标
-
+使用该函数模拟连续点击时可能有点击速度过慢的问题,这时可以用press()函数代替。
longClick(x, y)#
模拟长按坐标(x, y), 并 返回是否成功。只有在长按执行完成(大约600毫秒)时脚本才会继续执行。
+模拟长按坐标(x, y), 并返回是否成功。只有在长按执行完成(大约600毫秒)时脚本才会继续执行。
一般而言,只有长按过程中被其他事件中断(例如用户自行点击)才会长按失败。
press(x, y, duration)#
@@ -865,7 +994,13 @@ toast(n + 1);
模拟按住坐标(x, y), 并返回是否成功。只有按住操作执行完成时脚本才会继续执行。
如果按住时间过短,那么会被系统认为是点击;如果时长超过500毫秒,则认为是长按。
一般而言,只有按住过程中被其他事件中断才会操作失败。
-swipe(x1, y1, x2, y2, duration)#
+一个连点器的例子如下:
+//循环100次
+for(var i = 0; i < 100; i++){
+ //点击位置(500, 1000), 每次用时1毫秒
+ press(500, 1000, 1);
+}
+
swipe(x1, y1, x2, y2, duration)#
gestures([0, 500, [800, 300], [500, 1000]],
[0, 500, [300, 1500], [500, 1000]]);
-
setScreenMetrics(width, height)#
-设置脚本坐标点击所适合的屏幕宽高。如果脚本运行时,屏幕宽度不一致会自动放缩坐标。
-例如在1920*1080的设备中,某个操作的代码为
-setScreenMetrics(1080, 1920);
-click(800, 200);
-longClick(300, 500);
-
那么在其他设备上AutoJs会自动放缩坐标以便脚本仍然有效。
-RootAutomator#
+RootAutomator#
Stability: 2 - StableRootAutomator是一个使用root权限来模拟触摸的对象,用它可以完成触摸与多点触摸,并且这些动作的执行没有延迟。
-注意以下命令需要root权限
+一个脚本中最好只存在一个RootAutomator,并且保证脚本结束退出他。可以在exit事件中退出RootAutomator,例如:
var ra = new RootAutomator();
-
RootAutomator.tap(x, y[, id])#
+events.on('exit', function(){
+ ra.exit();
+});
+//执行一些点击操作
+...
+注意以下命令需要root权限
+RootAutomator.tap(x, y[, id])#
x <number> 横坐标
y <number> 纵坐标
@@ -916,6 +1046,7 @@ ra.tap(200, 200, 2);
ra.exit();
如果不需要多点触摸,则不需要id这个参数。
多点触摸通常用于手势或游戏操作,例如模拟双指捏合、双指上滑等。
+某些情况下可能存在tap点击无反应的情况,这时可以用RootAutomator.press()函数代替。
RootAutomator.swipe(x1, x2, y1, y2[, duration, id])#
x1 <number> 滑动起点横坐标
@@ -928,43 +1059,42 @@ ra.exit();
模拟一次从(x1, y1)到(x2, y2)的时间为duration毫秒的滑动。
RootAutomator.press(x, y, duration[, id])#
模拟按下位置(x, y),时长为duration毫秒。
-使用该函数模拟连续点击时可能有点击速度过慢的问题,这时可以用[RootAutomator.press][]函数代替。
RootAutomator.longPress(x, y[\, id])#
模拟长按位置(x, y)。
以上为简单模拟触摸操作的函数。如果要模拟一些复杂的手势,需要更底层的函数。
-RootAutomator.touchDown(x, y[\, id])#
+RootAutomator.touchDown(x, y[, id])#
模拟手指按下位置(x, y)。
-RootAutomator.touchMove(x, y[\, id])#
+RootAutomator.touchMove(x, y[, id])#
模拟移动手指到位置(x, y)。
RootAutomator.touchUp([id])#
模拟手指弹起。
使用root权限点击和滑动的简单命令#
-Stability: 1 - Experimental 注意:本章节的函数在后续版本很可能有改动!请勿过分依赖本章节函数的副作用。推荐使用[RootAutomator][]代替本章节的触摸函数。
+Stability: 1 - Experimental 注意:本章节的函数在后续版本很可能有改动!请勿过分依赖本章节函数的副作用。推荐使用RootAutomator代替本章节的触摸函数。
以下函数均需要root权限,可以实现任意位置的点击、滑动等。
- 这些函数通常首字母大写以表示其特殊的权限。
@@ -1000,17 +1130,17 @@ sleep(500);
Device#
Stability: 2 - Stabledevice模块提供了与设备有关的信息与操作,例如获取设备宽高,内存使用率,IMEI,调整设备亮度、音量等。
-此模块的部分函数,例如调整音量,需要"修改系统设置"的权限。如果没有该权限,会抛出异常并跳转到权限设置界面。
+此模块的部分函数,例如调整音量,需要"修改系统设置"的权限。如果没有该权限,会抛出SecurityException并跳转到权限设置界面。
device.width#
-设备宽度。例如1080。
+
设备屏幕分辨率宽度。例如1080。
device.height#
-设备高度。例如1920。
+设备屏幕分辨率高度。例如1920。
device.buildId#
- <string>
@@ -1215,158 +1345,272 @@ sleep(500);
Dialogs#
-Stability: 2 - Stabledialogs 模块允许用户通过对话框与脚本进行交互。
-dialogs.rawInput(title[, default])#
-显示一个包含输入框的对话框。如果指定了 default ,那么对话框显示时输入框的内容就为该值。
-该函数也可通过 rawInput(title[, default]) 、 prompt(title[, default]) 或者 dialogs.prompt(title[, default]) 来调用。调用时脚本将阻塞直至对话框被关闭。如果用户输入内容并点击确定,函数将返回输入的内容;否则返回 null 。
-dialogs.input(title[, default])#
-等效于 eval(dialogs.rawInput(title, default))
-dialogs.prompt(title[, default])#
-见 dialogs.rawInput
-dialogs.alert(title[, content])#
+Stability: 2 - Stabledialogs 模块提供了简单的对话框支持,可以通过对话框和用户进行交互。最简单的例子如下:
+
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(title[, content]) 来调用。调用时脚本将阻塞直至对话框被关闭。该函数无返回值。
-dialogs.confirm(title[, content])#
+显示一个只包含“确定”按钮的提示对话框。直至用户点击确定脚本才继续运行。
+该函数也可以作为全局函数使用。
+alert("出现错误~", "出现未知错误,请联系脚本作者”);
+
在ui模式下该函数返回一个Promise。例如:
+"ui";
+alert("嘿嘿嘿").then(()=>{
+ //当点击确定后会执行这里
+});
+
dialogs.confirm(title[, content, callback])#
title <string> 对话框的标题。
content <string> 可选,对话框的内容。默认为空。
+callback <Function> 回调函数,可选。当用户点击确定时被调用,一般用于ui模式。
-显示一个包含“确定”和“取消”按钮的提示对话框。
-该函数也可通过 confirm(title[, content]) 来调用。调用时脚本将阻塞直至对话框被关闭。如果用户点击“确定”则返回 true ,否则返回 false 。
-dialogs.select(title, items)#
+显示一个包含“确定”和“取消”按钮的提示对话框。如果用户点击“确定”则返回 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模式。
-显示一个带有选项列表的对话框。
-该函数也可通过 dialogs.select(title, ...items) 来调用。
例如: dialogs.select("标题", ["A", "B", "C"]) 可以用 dialogs.select("标题", "A", "B", "C") 替代。
-调用时脚本将阻塞直至对话框被关闭。如果用户点击了对话框中的某个选项,该函数会返回该选项的位置(选中第一个选项返回0,第二个选项返回1,以此类推),否则返回-1。
-dialogs.singleChoice(title, items[, index])#
+显示一个带有选项列表的对话框,等待用户选择,返回用户选择的选项索引(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。
+index <number> 对话框的初始选项的位置,默认为0。
+callback <Function> 回调函数,可选。当用户点击确定时被调用,一般用于ui模式。
-显示一个带有单选框选项列表的对话框。
-调用时脚本将阻塞直至对话框被关闭。如果用户选中了对话框中的某个选项并点击“确定”,该函数会返回该选项的位置(选中第一个选项返回0,第二个选项返回1,以此类推),否则返回-1。
-dialogs.multiChoice(title, items[, indexes])#
+显示一个单选列表对话框,等待用户选择,返回用户选择的选项索引(0 ~ item.length - 1)。如果用户取消了选择,返回-1。
+在ui模式下该函数返回一个Promise。
+dialogs.multiChoice(title, items[, indices, callback])#
title <string> 对话框的标题。
items <Array> 对话框的选项列表,是一个字符串数组。
-indexes <Array> 可选,对话框的默认选项的位置数组。默认为空数组。
+indices <Array> 选项列表中初始选中的项目索引的数组,默认为空数组。
+callback <Function> 回调函数,可选。当用户点击确定时被调用,一般用于ui模式。
-显示一个带有多选框选项列表的对话框。
-调用时脚本将阻塞直至对话框被关闭。如果用户点击“确定”按钮,该函数会返回所有已选选项的位置组成的数组,否则返回空数组。
+
显示一个多选列表对话框,等待用户选择,返回用户选择的选项索引的数组。如果用户取消了选择,返回[]。
+在ui模式下该函数返回一个Promise。
Engines#
-Stability: 2 - Stableengines模块包含了一些与脚本引擎有关的函数,包括运行其他脚本,关闭脚本等。
-engines.execScript(name, script[, config])#
+Stability: 2 - Stableengines模块包含了一些与脚本环境、脚本运行、脚本引擎有关的函数,包括运行其他脚本,关闭脚本等。
+例如,获取脚本所在目录:
+toast(engines.myEngine().cwd());
+
engines.execScript(name, script[, config])#
在新线程中运行脚本script。返回一个ScriptExectuion对象。
-engines.execScriptFile(path[, config])#
+在新的脚本环境中运行脚本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 \
在新线程中运行脚本文件path。返回一个ScriptExecution对象。
-engines.execAutoFile(path[, config])#
+在新的脚本环境中运行脚本文件path。返回一个ScriptExecution对象。
+engines.execScriptFile("/sdcard/脚本/1.js");
+
engines.execAutoFile(path[, config])#
path <string> 要运行的录制文件路径。
-config \
在新线程中运行录制文件path。返回一个ScriptExecution对象。
-engines.stopAll()#
+在新的脚本环境中运行录制文件path。返回一个ScriptExecution对象。
+engines.execAutoFile("/sdcard/脚本/1.auto");
+
engines.stopAll()#
停止所有正在运行的脚本。包括当前脚本自身。
engines.stopAllAndToast()#
停止所有正在运行的脚本并显示停止的脚本数量。包括当前脚本自身。
engines.myEngine()#
返回当前脚本的脚本引擎对象(ScriptEngine)
ScriptExecution#
-执行脚本时返回的对象,可以通过他获取执行的引擎、配置、源码等。
+执行脚本时返回的对象,可以通过他获取执行的引擎、配置等,也可以停止这个执行。
+要停止这个脚本的执行,使用exectuion.getEngine().forceStop().
ScriptExecution.getEngine()#
返回执行该脚本的脚本引擎对象(ScriptEngine)
ScriptExecution.getConfig()#
返回该脚本的运行配置(ScriptConfig)
-ScriptExecution.getSource()#
-返回该脚本的源码对象(ScriptSource)
ScriptEngine#
+脚本引擎对象。
ScriptEngine.forceStop()#
停止脚本引擎的执行。
-ScriptEngine.getTag(tagName)#
+ScriptEngine.cwd()#
返回对应于tagName的附加在该脚本引擎上的额外信息。tagName包括:
-
-source 该脚本引擎当前正在执行的源码(ScriptSource)
-execute_path 该脚本引擎当前执行的路径
-
-停止脚本引擎的执行。
+返回脚本执行的路径。对于一个脚本文件而言为这个脚本所在的文件夹;对于其他脚本,例如字符串脚本,则为null或者执行时的设置值。
ScriptConfig#
脚本执行时的配置。
delay#
-延迟执行的毫秒数
+
+- <number>
+
+延迟执行的毫秒数
interval#
-循环运行时两次运行之间的时间间隔
+
+- <number>
+
+循环运行时两次运行之间的时间间隔
loopTimes#
-循环运行次数
+
+- <number>
+
+循环运行次数
getPath()#
-返回一个字符串数组表示脚本运行时模块寻找的路径。
-ScriptSource#
-脚本执行时的源码对象。可以是字符串源码、文件源码等。
-如果该源码是文件脚本,则可以通过toString()得到该文件的路径。
-getName()#
-返回该源码的名称。
-getEngineName()#
-返回执行该源码的脚本引擎的名称。
+
+- 返回 <Array>
+
+返回一个字符串数组表示脚本运行时模块寻找的路径。
Events#
Stability: 2 - Stableevents模块提供了监听手机通知、按键、触摸的接口。您可以用他配合自动操作函数完成自动化工作。
events本身是一个EventEmiiter, 但内置了一些事件、包括按键事件、通知事件、Toast事件等。
-events.emitter()#
-返回一个新的[EventEmitter][]。这个EventEmitter没有内置任何事件。
+需要注意的是,事件的处理是单线程的,并且仍然在原线程执行,如果脚本主体或者其他事件处理中有耗时操作、轮询等,则事件将无法得到及时处理(会进入事件队列等待脚本主体或其他事件处理完成才执行)。例如:
+auto();
+events.observeNotification();
+events.on('toast', function(t){
+ //这段代码将得不到执行
+ log(t);
+});
+while(true){
+ //死循环
+}
+
events.emitter()#
+返回一个新的EventEmitter。这个EventEmitter没有内置任何事件。
events.observeKey()#
-启用按键监听,例如音量键、Home键。此函数使用无障碍服务实现,因此此函数会调用auto()确保无障碍服务启用。
-只有这个函数成功执行后, [onKeyDown][], [onKeyUp][]等按键事件的监听才有效。
+启用按键监听,例如音量键、Home键。按键监听使用无障碍服务实现,如果无障碍服务未启用会抛出异常并提示开启。
+只有这个函数成功执行后, onKeyDown, onKeyUp等按键事件的监听才有效。
该函数在安卓4.3以上才能使用。
events.onKeyDown(keyName, listener)#
keyName <string> 要监听的按键名称
listener <Function> 按键监听器。参数为一个KeyEvent。
-注册一个按键监听函数,当有keyName对应的按键被按下会调用该函数。可用的按键名称参见[Keys][]。
+注册一个按键监听函数,当有keyName对应的按键被按下会调用该函数。可用的按键名称参见Keys。
例如:
//启用按键监听
events.observeKey();
@@ -1425,7 +1669,7 @@ events.onKeyDown("home", function(event){
events.observeTouch()#
启用屏幕触摸监听。(需要root权限)
只有这个函数被成功执行后, 触摸事件的监听才有效。
-没有root权限调用该函数则什么也不会发生。(注意: 这个行为未来可能会更改为抛出异常)
+没有root权限调用该函数则什么也不会发生。
events.setTouchEventTimeout(timeout)#
timeout <number> 两个触摸事件的最小间隔。单位毫秒。默认为10毫秒。如果number小于0,视为0处理。
@@ -1437,7 +1681,7 @@ events.onKeyDown("home", function(event){
返回触摸事件的最小时间间隔。
events.onTouch(listener)#
-listener <Function> 参数为[Point][]的函数
+listener <Function> 参数为Point的函数
注册一个触摸监听函数。相当于on("touch", listener)。
例如:
@@ -1464,13 +1708,21 @@ events.on("key", function(keyCode, event){
});
其中监听器的参数KeyCode包括:
-KeyEvent.KEYCODE_HOME 主页键
-KeyEvent.KEYCODE_BACK 返回键
-KeyEvent.KEYCODE_MENU 菜单键
-KeyEvent.KEYCODE_VOLUMEUP 音量上键
-KeyEvent.KEYCODE_VOLUMEDOWN 音量下键
+keys.home 主页键
+keys.back 返回键
+keys.menu 菜单键
+keys.volume_up 音量上键
+keys.volume_down 音量下键
-事件: 'key_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> 事件
@@ -1501,18 +1753,18 @@ events.on("exit", function(){
log("结束运行");
});
log("即将结束运行");
-obverseNotification()#
-开启通知(包括Toast)监听。
-通知与Toast监听依赖于无障碍服务,因此这个函数会调用auto()来确保无障碍服务启用。
+events.observeNotification()#
+开启通知监听。例如QQ消息、微信消息、推送等通知。
+通知监听依赖于通知服务,如果通知服务没有运行,会抛出异常并跳转到通知权限开启界面。(有时即使通知权限已经开启通知服务也没有运行,这时需要关闭权限再重新开启一次)
例如:
events.obverseNotification();
events.onNotification(function(notification){
log(notification.getText());
});
-events.onToast(function(toast){
- log(toast.getText());
-});
-
事件: 'toast'#
+events.observeToast()#
+开启Toast监听。
+Toast监听依赖于无障碍服务,因此此函数会确保无障碍服务运行。
+事件: 'toast'#
toast <Object>
getText() 获取Toast的文本内容
@@ -1520,23 +1772,91 @@ events.onToast(function(toast){
-当有应用发出toast(气泡消息)时会触发该事件。但Auto.js软件本身的toast除外。
-例如,要记录发出所有toast的应用:
-events.obverseNotification();
+
当有应用发出toast(气泡消息)时会触发该事件。但Auto.js软件本身的toast除外。
+例如,要记录发出所有toast的应用:
+events.observeToast();
events.onToast(function(toast){
log("Toast内容: " + toast.getText() + " 包名: " + toast.getPackageName());
});
事件: 'notification'#
-notification <Object> 通知
+notification Notification 通知对象
-当有应用发出通知时会触发该事件。
+当有应用发出通知时会触发该事件,参数为Notification。
例如:
events.observeNotification();
-events.on("notification", function(notification){
- log(notification);
+events.on("notification", function(n){
+ log("收到新通知:\n 标题: %s, 内容: %s, \n包名: %s", n.getTitle(), n.getText(), n.getPackageName());
});
-
注意: 这是一个实验性功能。实测只有某些情况下的通知才能被正确捕捉。
+
Notification#
+通知对象,可以获取通知详情,包括通知标题、内容、发出通知的包名、时间等,也可以对通知进行操作,比如点击、删除。
+Notification.number#
+
+- <number>
+
+通知数量。例如QQ连续收到两条消息时number为2。
+Notification.when#
+
+- <number>
+
+通知发出时间的时间戳,可以用于构造Date对象。例如:
+events.observeNotification();
+events.on("notification", function(n){
+ log("通知时间为}" + new Date(n.when));
+});
+
Notification.getPackageName()#
+
+- 返回 <string>
+
+获取发出通知的应用包名。
+Notification.getTitle()#
+
+- 返回 <string>
+
+获取通知的标题。
+Notification.getText()#
+
+- 返回 <string>
+
+获取通知的内容。
+Notification.click()#
+点击该通知。例如对于一条QQ消息,点击会进入具体的聊天界面。
+Notification.delete()#
+删除该通知。该通知将从通知栏中消失。
+KeyEvent#
+Stability: 2 - StableKeyEvent.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()#
+
+- 返回 <number>
+
+返回事件发生的时间戳。
+KeyEvent.getDownTime()#
+返回最近一次按下事件的时间戳。如果本身是按下事件,则与getEventTime()相同。
+KeyEvent.keyCodeToString(keyCode)#
+把键值转换为字符串。例如KEYCODE_HOME转换为"KEYCODE_HOME"。
+keys#
+Stability: 2 - Stable按键事件中所有可用的按键名称为:
+
+volume_up 音量上键
+volume_down 音量下键
+home 主屏幕键
+back 返回键
+menu 菜单键
+
EventEmitter#
Stability: 2 - StableEventEmitter.defaultMaxListeners#
每个事件默认可以注册最多 10 个监听器。 单个 EventEmitter 实例的限制可以使用 emitter.setMaxListeners(n) 方法改变。 所有 EventEmitter 实例的默认值可以使用 EventEmitter.defaultMaxListeners 属性改变。
@@ -1700,37 +2020,6 @@ myEmitter.emit('event');
默认情况下,如果为特定事件添加了超过 10 个监听器,则 EventEmitter 会打印一个警告。 此限制有助于寻找内存泄露。 但是,并不是所有的事件都要被限为 10 个。 emitter.setMaxListeners() 方法允许修改指定的 EventEmitter 实例的限制。 值设为 Infinity(或 0)表明不限制监听器的数量。
返回一个 EventEmitter 引用,可以链式调用。
-KeyEvent#
-Stability: 2 - StableKeyEvent.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()#
-返回事件发生的时间戳。返回值的类型是number。
-KeyEvent.getDownTime()#
-返回最近一次按下事件的时间戳。如果本身是按下事件,则与getEventTime()相同。
-KeyEvent.keyCodeToString(keyCode)#
-把键值转换为字符串。例如KEYCODE_HOME转换为"KEYCODE_HOME"。
-Keys#
-Stability: 2 - Stable按键事件中所有可用的按键名称为:
-
-volume_up 音量上键
-volume_down 音量下键
-home 主屏幕键
-back 返回键
-menu 菜单键
-
Floaty#
@@ -1833,68 +2122,97 @@ w.exit.click(()=> w.close());
Files#
Stability: 2 - Stablefiles模块提供了一些常见的文件处理,包括文件读写、移动、复制、删掉等。
+一次性的文件读写可以直接使用files.read(), files.write(), files.append()等方便的函数,但如果需要频繁读写或随机读写,则使用open()函数打开一个文件对象来操作文件,并在操作完毕后调用close()函数关闭文件。
files.isFile(path)#
返回路径path是否是文件。
-files.isDir(path)#
+log(files.isDir("/sdcard/文件夹/")); //返回false
+log(files.isDir("/sdcard/文件.txt")); //返回true
+
files.isDir(path)#
返回路径path是否是文件夹。
-files.isEmptyDir(path)#
+log(files.isDir("/sdcard/文件夹/")); //返回true
+log(files.isDir("/sdcard/文件.txt")); //返回false
+
files.isEmptyDir(path)#
返回文件夹path是否为空文件夹。如果该路径并非文件夹,则直接返回false。
+返回文件夹path是否为空文件夹。如果该路径并非文件夹,则直接返回false。
files.join(parent, child)#
连接两个路径并返回,例如files.join("/sdcard/", "1.txt")返回"/sdcard/1.txt"。
files.create(path)#
创建一个文件并返回是否创建成功。
-files.createIfNotExists(path)#
+创建一个文件或文件夹并返回是否创建成功。如果文件已经存在,则直接返回false。
+files.create("/sdcard/新文件夹/");
+
files.createWithDirs(path)#
创建一个文件并返回是否创建成功。
-files.exists(path)#
+创建一个文件或文件夹并返回是否创建成功。如果文件所在文件夹不存在,则先创建他所在的一系列文件夹。如果文件已经存在,则直接返回false。
+files.createWithDirs("/sdcard/新文件夹/新文件夹/新文件夹/1.txt");
+
files.exists(path)#
返回在路径path处的文件是否存在。
files.ensureDir(path)#
path <string> 路径
-确保路径path所在的文件夹存在。
+确保路径path所在的文件夹存在。如果该路径所在文件夹不存在,则创建该文件夹。
例如对于路径"/sdcard/Download/ABC/1.txt",如果/Download/文件夹不存在,则会先创建Download,再创建ABC文件夹。
files.read(path[, encoding = "utf-8"])#
读取文本文件path的所有内容并返回一个字符串。
-files.readBytes(path)#
+读取文本文件path的所有内容并返回。如果文件不存在,则抛出FileNotFoundException。
+log(files.read("/sdcard/1.txt"));
+
files.readBytes(path)#
path <string> 路径
+- 返回 <byte[]>
-读取文件path的所有内容并返回一个字节数组。
-注意,该数组是Java的数组,不具有JavaScript数组的函数。
-files.write(path, text[, encoding = "utf-8"])#
+读取文件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中。如果文件存在则覆盖,不存在则创建。
-files.writeBytes(path, bytes)#
+var text = "文件内容";
+//写入文件
+files.write("/sdcard/1.txt", text);
+//用其他应用查看文件
+app.viewFile("/sdcard/1.txt");
+
files.writeBytes(path, bytes)#
把text追加到文件path的末尾。如果文件不存在则创建。
-files.appendBytes(path, text[, encoding = 'utf-8'])#
+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[]> 字节数组,要写入的二进制数据
@@ -1917,75 +2240,99 @@ w.exit.click(()=> w.close());
复制文件。例如files.copy("/sdcard/1.txt", "/sdcard/Download/1.txt")。
+
复制文件,返回是否复制成功。例如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.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卡,即外部存储器。
+
+- 返回 <string>
+
+返回SD卡路径。所谓SD卡,即外部存储器。
+files.cwd()#
+
+- 返回 <string>
+
+返回脚本的"当前工作文件夹路径"。该路径指的是,如果脚本本身为脚本文件,则返回这个脚本文件所在目录;否则返回null获取其他设定路径。
+例如,对于脚本文件"/sdcard/脚本/1.js"运行files.cwd()返回"/sdcard/脚本/"。
files.listDir(path[, filter])#
path <string> 路径
-filter <Function> 过滤函数,可选。接收一个String参数(文件名),返回一个Boolean值。
+filter <Function> 过滤函数,可选。接收一个string参数(文件名),返回一个boolean值。
列出文件夹path下的满足条件的文件和文件夹的名称的数组。如果不加filter参数,则返回所有文件和文件夹。
-例如,获取sdcard目录下的txt文件为
-var txtFiles = files.listDir("/sdcard/", function(name){
- return name.endsWith(".txt") && files.isFile("/sdcard/" + name);
+列出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": 附加模式。该模式下将会把写入的文本附加到文件末尾。
目前暂不支持二进制模式,随机读写模式。
+- "r": 只读文本模式。该模式下只能对文件执行文本读取操作。
+- "w": 只写文本模式。该模式下只能对文件执行文本覆盖写入操作。
+- "a": 附加文本模式。该模式下将会把写入的文本附加到文件末尾。
+- "rw": 随机读写文本模式。该模式下将会把写入的文本附加到文件末尾。
目前暂不支持二进制模式,随机读写模式。
encoding <string> 字符编码。
-bufferSize <Number> 文件读写的缓冲区大小。
+bufferSize <number> 文件读写的缓冲区大小。
打开一个文件。根据打开模式返回不同的文件对象。包括:
@@ -2034,64 +2381,51 @@ w.exit.click(()=> w.close());
全局变量与函数#
-全局变量和函数在所有模块中均可使用。 但以下变量的作用域只在模块内,详见 module文档:
+全局变量和函数在所有模块中均可使用。 但以下变量的作用域只在模块内,详见 module:
- exports
- module
- require()
以下的对象是特定于 Auto.js 的。 有些内置对象是 JavaScript 语言本身的一部分,它们也是全局的。
-一些模块中的函数为了使用方便也可以直接全局使用,这些函数在此不再赘述。例如timers模块的setInterval等函数。
-sleep([n])#
+一些模块中的函数为了使用方便也可以直接全局使用,这些函数在此不再赘述。例如timers模块的setInterval, setTimeout等函数。
+sleep(n)#
n <number> 毫秒数
暂停运行n毫秒的时间。1秒等于1000毫秒。
-launchPackage(packageName)#
-
-packageName <string> 应用包名
-
-运行包名为packageName的应用主界面(Launcher)。例如,打开微信为:
-
launchPackage("com.tencent.mm");
-
如果存在多个应用包名相同的情况(如双开应用),如何处理取决于操作系统。在MIUI中会弹出多开应用的选择界面。
-launchApp(appName)#
-
-appName <String> 应用名称
-
-运行应用名称为appName的应用主界面。当有应用名称相同时只运行其中某一个。
例如,打开微信为:
-launchApp("微信");
+//暂停5毫秒
+sleep(5000);
currentPackage()#
-返回最近一次监测到的正在运行的应用的包名,一般可以认为就是当前正在运行的应用的包名。
+
+- 返回 <string>
+
+返回最近一次监测到的正在运行的应用的包名,一般可以认为就是当前正在运行的应用的包名。
+此函数依赖于无障碍服务,如果服务未启动,则抛出异常并提示用户启动。
currentActivity()#
-返回最近一次监测到的正在运行的Activity的名称,一般可以认为就是当前正在运行的Activity的名称。
-getPackageName(appName)#
获取应用的包名。例如getPackageName("QQ")为"com.tencent.mobileqq"。如果有相同名称的应用,只返回其中某一个的包名。如果不存在这个名称的应用,会返回null。
-getAppName(packageName)#
-
-packageName <string> 应用包名
-
-返回对应包名的应用的名称。如果应用不存在,返回null。
-openAppSetting(packageName)#
-
-packageName <string> 应用包名
-
-打开某个应用的应用详情页,也就是管理应用权限和可以停止其运行的页面。如果应用包名不存在,则返回false;否则返回true。
+返回最近一次监测到的正在运行的Activity的名称,一般可以认为就是当前正在运行的Activity的名称。
+此函数依赖于无障碍服务,如果服务未启动,则抛出异常并提示用户启动。
setClip(text)#
text <string> 文本
设置剪贴板内容。此剪贴板即系统剪贴板,在一般应用的输入框中"粘贴"既可使用。
-getClip()#
-返回系统剪贴板的内容。
-toast(message)#
+setClip("剪贴板文本");
+
getClip()#
-- message <string>> | <{Object> 要显示的信息
+- 返回 <string>
+
+返回系统剪贴板的内容。
+toast("剪贴板内容为:" + getClip());
+
toast(message)#
+
+- message <string> 要显示的信息
以气泡显示信息message几秒。(具体时间取决于安卓系统,一般都是2秒)
-注意,信息的显示是"异步"执行的(不属于Looper循环),并且,不会等待信息消失程序才继续执行。如果在循环中执行该命令,可能出现脚本停止运行后仍然有不断的气泡信息出现的情况。
+
注意,信息的显示是"异步"执行的,并且,不会等待信息消失程序才继续执行。如果在循环中执行该命令,可能出现脚本停止运行后仍然有不断的气泡信息出现的情况。
例如:
for(var i = 0; i < 100; i++){
toast(i);
@@ -2110,11 +2444,10 @@ toast = function(message){
}
for(var i = 0; i < 100; i++){
toast(i);
- sleep(2000);
}
toastLog(message)#
-- message \
| \
+- message <string> 要显示的信息
相当于toast(message);log(message)。显示信息message并在控制台中输出。参见console.log。
waitForActivity(activity[, period = 200])#
@@ -2131,14 +2464,19 @@ for(var i = 0; i < 100; i++){
等待指定的应用出现。例如waitForPackage("com.tencent.mm")为等待当前界面为微信。
exit()#
立即停止脚本运行。
+立即停止是通过抛出ScriptInterrupttedException来实现的,因此如果用try...catch把exit()函数的异常捕捉,则脚本不会立即停止,仍会运行几行后再停止。
random(min, max)#
返回一个在[min...max]之间的随机数。例如random(0, 2)可能产生0, 1, 2.
+返回一个在[min...max]之间的随机数。例如random(0, 2)可能产生0, 1, 2。
random()#
-返回在[0, 1)的随机浮点数。
+
+- 返回 <number>
+
+返回在[0, 1)的随机浮点数。
context#
全局变量。一个android.content.Context对象。
注意该对象为ApplicationContext,因此不能用于界面、对话框等的创建。
@@ -2628,27 +2966,49 @@ if(p){
Keys#
按键模拟部分提供了一些模拟物理按键的全局函数,包括Home、音量键、照相键等,有的函数依赖于无障碍服务,有的函数依赖于root权限。
+一般来说,以大写字母开头的函数都依赖于root权限。执行此类函数时,如果没有root权限,则函数执行后没有效果,并会在控制台输出一个警告。
back()#
-模拟按下返回键。返回是否执行成功。
+
+- 返回 <boolean>
+
+模拟按下返回键。返回是否执行成功。
此函数依赖于无障碍服务。
home()#
-模拟按下Home键。返回是否执行成功。
+
+- 返回 <boolean>
+
+模拟按下Home键。返回是否执行成功。
此函数依赖于无障碍服务。
powerDialog()#
-弹出电源键菜单。返回是否执行成功。
+
+- 返回 <boolean>
+
+弹出电源键菜单。返回是否执行成功。
此函数依赖于无障碍服务。
notifications()#
-弹出通知栏。返回是否执行成功。
+
+- 返回 <boolean>
+
+拉出通知栏。返回是否执行成功。
此函数依赖于无障碍服务。
quickSettings()#
-显示快速设置(下拉通知栏到底)。返回是否执行成功。
+
+- 返回 <boolean>
+
+显示快速设置(下拉通知栏到底)。返回是否执行成功。
此函数依赖于无障碍服务。
recents()#
-显示最近任务。返回是否执行成功。
+
+- 返回 <boolean>
+
+显示最近任务。返回是否执行成功。
此函数依赖于无障碍服务。
splitScreen()#
-分屏。返回是否执行成功。
-此函数依赖于无障碍服务。
+
+- 返回 <boolean>
+
+分屏。返回是否执行成功。
+此函数依赖于无障碍服务, 并且需要系统自身功能的支持。
Home()#
模拟按下Home键。
此函数依赖于root权限。
@@ -2788,7 +3148,7 @@ if(p){
module (模块)#
Stability: 2 - StableAuto.js 有一个简单的模块加载系统。 在 Auto.js 中,文件和模块是一一对应的(每个文件被视为一个独立的模块)。
例子,假设有一个名为 foo.js 的文件:
-const circle = require('./circle.js');
+const circle = require('circle.js');
console.log("半径为 4 的圆的面积是 %d", circle.area(4));
在第一行中,foo.js 加载了同一目录下的 circle.js 模块。
circle.js 文件的内容为:
@@ -2802,12 +3162,12 @@ circle.circumference = (r) => 2 * PI * r;
module.exports = circle;
circle.js 模块导出了 area() 和 circumference() 两个函数。 通过在特殊的 exports 对象上指定额外的属性,函数和对象可以被添加到模块的根部。
-模块内的本地变量是私有的,因为模块被 Node.js 包装在一个函数中(详见模块包装器)。 在这个例子中,变量 PI 是 circle.js 私有的。
+模块内的本地变量是私有的。 在这个例子中,变量 PI 是 circle.js 私有的,不会影响到加载他的脚本的变量环境。
module.exports属性可以被赋予一个新的值(例如函数或对象)。
如下,bar.js 会用到 square 模块,square 导出一个构造函数:
-const square = require('./square.js');
+const square = require('square.js');
const mySquare = square(2);
-console.log(`正方形的面积是 ${mySquare.area()}`);
+console.log("正方形的面积是 %d", mySquare.area());
square 模块定义在 square.js 中:
// 赋值给 `exports` 不会修改模块,必须使用 `module.exports`
@@ -3346,11 +3706,11 @@ var clickableNames = names.find(clickable());
cmd <string> 要执行的命令
root <Boolean> 是否以root权限运行,默认为false。
-返回运行一个对象表示命令的执行结果。其属性如下:
+一次性执行命令cmd, 并返回命令的执行结果。返回对象的其属性如下:
- code <number> 返回码。执行成功时为0,失败时为非0的数字。
- result <string> 运行结果(stdout输出结果)
-- error \
运行的错误信息(stderr输出结果)。例如执行需要root权限的命令但没有授予root权限会返回错误信息"Permission denied"。
+- error <string> 运行的错误信息(stderr输出结果)。例如执行需要root权限的命令但没有授予root权限会返回错误信息"Permission denied"。
示例(强制停止微信) :
var result = shell("am force-stop com.tencent.mm", true);
@@ -3369,6 +3729,7 @@ if(result.code == 0){
Shell对象的"构造函数"。
var sh = new Shell(true);
+//强制停止微信
sh.exec("am force-stop com.tencent.mm");
sh.exit();
Shell.exec(cmd)#
@@ -3379,7 +3740,7 @@ sh.exit();
注意,命令执行是"异步"的、非阻塞的。也就是不会等待命令完成后才继续向下执行。
尽管这样的设计使用起来有很多不便之处,但受限于终端模拟器,暂时没有解决方式;如果后续能找到解决方案,则将提供Shell.execAndWaitFor函数。
Shell.exit()#
-直接退出shell。这意味着正在执行的命令会被强制退出。
+直接退出shell。正在执行的命令会被强制退出。
Shell.exitAndWaitFor()#
执行"exit"命令并等待执行命令执行完成、退出shell。
此函数会执行exit命令来正常退出shell。
@@ -3389,21 +3750,24 @@ sh.exit();
设置该Shell的回调函数,以便监听Shell的输出。可以包括以下属性:
-- onOutput <function> 每当shell有新的输出时便会调用该函数。其参数是一个字符串。
-- onNewLine <function> 每当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();
@@ -3411,7 +3775,7 @@ sh.exit();
以下关于shell命令的资料来自AndroidStudio用户指南:Shell命令。
am命令#
am命令即Activity Manager命令,用于管理应用程序活动、服务等。
-以下命令均以"am "开头,例如"shell(\"am start -p com.tencent.mm\");"(启动微信)
+以下命令均以"am "开头,例如shell('am start -p com.tencent.mm');(启动微信)
start [options] intent#
启动 intent 指定的 Activity(应用程序活动)。
请参阅 intent 参数的规范。
选项包括:
@@ -3661,7 +4025,7 @@ 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)#
@@ -3699,35 +4063,325 @@ log("a = " + storage.get("a"));
Threads#
-Stability: 1 - Experimentthreads模块提供了多线程支持。可以启动新线程来运行脚本。新线程会在脚本停止时也自动停止。
-但是,在新线程中暂时不能使用timers模块的函数,包括setTimeout, setInterval等。而且目前在新线程调用exit()函数时只会退出当前线程。
+Stability: 1 - Experimentthreads模块提供了多线程支持,可以启动新线程来运行脚本。
+脚本主线程会等待所有子线程执行完成后才停止执行,因此如果子线程中有死循环,请在必要的时候调用exit()来直接停止脚本或threads.shutDownAll()来停止所有子线程。
+通过threads.start()启动的所有线程会在脚本被强制停止时自动停止。
+由于JavaScript自身没有多线程的支持,因此您可能会遇到意料之外的问题。
threads.start(action)#
action <Function> 要在新线程执行的函数
+- 返回 Thread
启动一个新线程并执行action。
例如:
threads.start(function(){
+ //在新线程执行的代码
while(true){
- log("线程2");
+ log("子线程");
}
});
while(true){
- log("线程1");
+ 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])#
+
+区别在于, 该定时器会在该线程执行。如果当前线程仍未开始执行或已经执行结束,则抛出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])#
+
+区别在于, 该定时器会在该线程执行。如果当前线程仍未开始执行或已经执行结束,则抛出IllegalStateException。
+Thread.setImmediate(callback[, ...args])#
+
+区别在于, 该定时器会在该线程执行。如果当前线程仍未开始执行或已经执行结束,则抛出IllegalStateException。
+Thread.clearInterval(id)#
+
+区别在于, 该定时器会在该线程执行。如果当前线程仍未开始执行或已经执行结束,则抛出IllegalStateException。
+Thread.clearTimeout(id)#
+
+区别在于, 该定时器会在该线程执行。如果当前线程仍未开始执行或已经执行结束,则抛出IllegalStateException。
+Thread.clearImmediate(id)#
+
+区别在于, 该定时器会在该线程执行。如果当前线程仍未开始执行或已经执行结束,则抛出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 - Stabletimers 模块暴露了一个全局的 API,用于在某个未来时间段调用调度函数。 因为定时器函数是全局的,所以使用该 API 无需调用 timers.*
Auto.js 中的计时器函数实现了与 Web 浏览器提供的定时器类似的 API,除了它使用了一个不同的内部实现,它是基于 Android Looper-Handler消息循环机制构建的。其实现机制与Node.js比较相似。
-setImmediate(callback[, ...args])#
-
-callback <Function> 在Looper循环的当前回合结束时要调用的函数。
-...args <any> 当调用 callback 时要传入的可选参数。
-
-预定立即执行的 callback,它是在 I/O 事件的回调之后被触发。 返回一个用于 clearImmediate() 的 id。
-当多次调用 setImmediate() 时,callback 函数会按照它们被创建的顺序依次执行。 每次事件循环迭代都会处理整个回调队列。 如果一个立即定时器是被一个正在执行的回调排入队列的,则该定时器直到下一次事件循环迭代才会被触发。
-setInterval(callback, delay[, ...args])#
+例如,要在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 之前要等待的毫秒数。
@@ -3744,22 +4398,38 @@ while(true){
预定在 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 对象。
-clearInterval(id)#
-
-id <number> 一个 setInterval() 返回的 id。
-
-取消一个由 setInterval() 创建的 Timeout 对象。
-clearTimeout(id)#
-
-id <number> 一个 setTimeout() 返回的 id。
-
-取消一个由 setTimeout() 创建的 Timeout 对象。
diff --git a/app/src/main/assets/docs/app.html b/app/src/main/assets/docs/app.html
index 3246ab92..aaee6b1a 100644
--- a/app/src/main/assets/docs/app.html
+++ b/app/src/main/assets/docs/app.html
@@ -72,15 +72,23 @@
目录
- App
+- app.launchApp(appName)
+- app.launch(packageName)
+- app.launchPackage(packageName)
+- app.getPackageName(appName)
+- app.getAppName(packageName)
+- app.openAppSetting(packageName)
- app.viewFile(path)
- app.editFile(path)
- app.uninstall(packageName)
- app.openUrl(url)
-- app.takePhoto(path)
- app.sendEmail(options)
-- app.intent(intent)
-- app.startActivity(intent)
-- app.sendBroadcast(intent)
+
+
+- 进阶: 意图Intent
@@ -89,77 +97,146 @@
App#
-app模块提供一系列函数,用于与其他应用的交互。例如打开文件、拍照、发送邮件等。
-同时提供了方便的基础函数startActivity和sendBroadcast,用他们可完成app模块没有内置的和其他应用的交互。
+app模块提供一系列函数,用于使用其他应用、与其他应用交互。例如发送意图、打开文件、发送邮件等。
+同时提供了方便的进阶函数startActivity和sendBroadcast,用他们可完成app模块没有内置的和其他应用的交互。
+app.launchApp(appName)#
+
+appName <string> 应用名称
+
+通过应用名称启动应用。如果该名称对应的应用不存在,则返回false; 否则返回true。如果该名称对应多个应用,则只启动其中某一个。
+该函数也可以作为全局函数使用。
+launchApp("Auto.js");
+
app.launch(packageName)#
+
+packageName <string> 应用包名
+
+通过应用包名启动应用。如果该包名对应的应用不存在,则返回false;否则返回true。
+该函数也可以作为全局函数使用。
+//启动微信
+launch("com.tencent.mm");
+
app.launchPackage(packageName)#
+
+packageName <string> 应用包名
+
+相当于app.launch(packageName)。
+app.getPackageName(appName)#
+
+appName <string> 应用名称
+
+获取应用名称对应的已安装的应用的包名。如果该找不到该应用,返回null;如果该名称对应多个应用,则只返回其中某一个的包名。
+该函数也可以作为全局函数使用。
+var name = getPackageName("QQ"); //返回"com.tencent.mobileqq"
+
app.getAppName(packageName)#
+
+packageName <string> 应用包名
+
+获取应用包名对应的已安装的应用的名称。如果该找不到该应用,返回null。
+该函数也可以作为全局函数使用。
+var name = getAppName("com.tencent.mobileqq"); //返回"QQ"
+
app.openAppSetting(packageName)#
+
+packageName <string> 应用包名
+
+打开应用的详情页(设置页)。如果找不到该应用,返回false; 否则返回true。
+该函数也可以作为全局函数使用。
app.viewFile(path)#
- path <string> 文件路径
-用其他应用查看文件。
-app.editFile(path)#
+用其他应用查看文件。文件不存在的情况由查看文件的应用处理。
+如果找不出可以查看该文件的应用,则抛出ActivityNotException。
+//查看文本文件
+app.viewFile("/sdcard/1.txt");
+
app.editFile(path)#
- path <string> 文件路径
-用其他应用编辑文件。
-app.uninstall(packageName)#
+用其他应用编辑文件。文件不存在的情况由编辑文件的应用处理。
+如果找不出可以编辑该文件的应用,则抛出ActivityNotException。
+//编辑文本文件
+app.editFile("/sdcard/1.txt/);
+
app.uninstall(packageName)#
卸载应用。
-app.openUrl(url)#
+卸载应用。执行后会会弹出卸载应用的提示框。如果该包名的应用未安装,由应用卸载程序处理,可能弹出"未找到应用"的提示。
+//卸载QQ
+app.uninstall("com.tencent.mobileqq");
+
app.openUrl(url)#
-- url <string> 网站的Url
+url <string> 网站的Url,如果不以"http://"或"https://"开头则默认是"http://"。
用浏览器打开网站url。
-app.takePhoto(path)#
-
-path <string> 照片保存路径
-
-调用相机应用拍照,完成后保存到路径path。
+如果没有安装浏览器应用,则抛出ActivityNotException。
app.sendEmail(options)#
options <Object> 发送邮件的参数。包括:
-- email <string> | <Array> 收件人的邮件地址。如果有多个收件人,则用字符串数组表示
-- cc <string> | <Array> 抄送收件人的邮件地址。如果有多个抄送收件人,则用字符串数组表示
-- bcc <string> | <Array> 密送收件人的邮件地址。如果有多个密送收件人,则用字符串数组表示
-- subject <string> 邮件主题(标题)
-- text <string> 邮件正文
-- attachment <string> | <Array> 附件的路径。如果有多个附件,则用字符串数组表示
+email <string> | <Array> 收件人的邮件地址。如果有多个收件人,则用字符串数组表示
+cc <string> | <Array> 抄送收件人的邮件地址。如果有多个抄送收件人,则用字符串数组表示
+bcc <string> | <Array> 密送收件人的邮件地址。如果有多个密送收件人,则用字符串数组表示
+subject <string> 邮件主题(标题)
+text <string> 邮件正文
+attachment <string> 附件的路径。
根据选项options调用邮箱应用发送邮件。这些选项均是可选的。
-app.intent(intent)#
+如果没有安装邮箱应用,则抛出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)#
-- intent <Object> 一个表示Intent对象,其属性可以包括:
-- action <string> 这个Intent的Action,比如"android.intent.action.SEND"
-- type <string> 这个Intent的MimeType,比如"text/plain"
-- data <string> 这个Intent的Data(Uri),可以是文件路径或者Url等。
-- category \
这个Intent的Category的字符串数组。
-- packageName <string> 目标包名
-- className <string> 目标Activity或Service等组件的名称
-- extras <Object> 以键值对构成的这个Intent的Extras。
+- 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对象构造的android.content.Intent对象。
+根据选项,构造一个意图Intent对象。
例如:
-var i = app.intent({
- action: "android.intent.action.VIEW",
- type: "text/plain",
- data: "file:///sdcard/1.txt",
+//打开应用来查看图片文件
+var i = app.intent({
+ action: "VIEW",
+ type: "image/png",
+ data: "file:///sdcard/1.png"
});
-
如果你看了一脸懵逼,请百度安卓Intent。
-app.startActivity(intent)#
+app.startAcvitity(i);
+
更多信息,请百度安卓Intent或参考Android指南: Intent。
+app.startActivity(options)#
相当于context.startActivity(intent)。
-app.sendBroadcast(intent)#
+根据选项构造一个Intent,并启动该Activity。
+app.sendBroadcast(options)#
相当于context.sendBroadcast(intent)。
+根据选项构造一个Intent,并发送该广播。
diff --git a/app/src/main/assets/docs/console.html b/app/src/main/assets/docs/console.html
index 8d85f9c2..82a30e68 100644
--- a/app/src/main/assets/docs/console.html
+++ b/app/src/main/assets/docs/console.html
@@ -114,6 +114,7 @@ console.log('count: %d', count);
console.log('count:', count);
// 打印: count: 5 到 stdout
详见 util.format()。
+该函数也可以作为全局函数使用。
console.verbose([data][, ...args])#
data
@@ -144,13 +145,15 @@ console.log('count:', count);
- message <string> value为false时要输出的信息
断言。如果value为false则输出错误信息message并停止脚本运行。
-console.input(data[, ...args])#
+var a = 1 + 1;
+console.assert(a == 2, "加法出错啦");
+
console.input(data[, ...args])#
data
...args
与console.log一样输出信息,并在控制台显示输入框等待输入。按控制台的确认按钮后会将输入的字符串用eval计算后返回。
-部分机型可能会有控制台不显示输入框的情况,属于bug。
+部分机型可能会有控制台不显示输入框的情况,属于bug。
例如:
var n = console.input("请输入一个数字:");
//输入123之后:
@@ -164,7 +167,7 @@ toast(n + 1);
与console.log一样输出信息,并在控制台显示输入框等待输入。按控制台的确认按钮后会将输入的字符串直接返回。
部分机型可能会有控制台不显示输入框的情况,属于bug。
例如:
-var n = console.input("请输入一个数字:");
+var n = console.rawInput("请输入一个数字:");
//输入123之后:
toast(n + 1);
//显示1231
@@ -174,17 +177,22 @@ toast(n + 1);
h <number> 高度
设置控制台的大小,单位像素。
-console.setPosition(x, y)#
+console.show();
+//设置控制台大小为屏幕的四分之一
+console.setSize(device.width / 2, device.height / 2);
+
console.setPosition(x, y)#
设置控制台的位置,单位像素。
-print(text)#
+console.show();
+console.setPosition(100, 100);
+
print(text)#
在控制台中输出文本text。不会自动换行。
+相当于log(text)。
diff --git a/app/src/main/assets/docs/coordinates-based-automation.html b/app/src/main/assets/docs/coordinates-based-automation.html
index 53fc8a92..25dffd2a 100644
--- a/app/src/main/assets/docs/coordinates-based-automation.html
+++ b/app/src/main/assets/docs/coordinates-based-automation.html
@@ -2,7 +2,7 @@
- Android7.0以上点按与手势模拟 | Auto.js 3.0.0 文档
+ 基于坐标的触摸模拟 | Auto.js 3.0.0 文档
@@ -71,14 +71,17 @@
目录
-- Android7.0以上点按与手势模拟
+- 基于坐标的触摸模拟
+
+- 安卓7.0以上的触摸和手势模拟
- RootAutomator
@@ -86,8 +89,8 @@
- RootAutomator.swipe(x1, x2, y1, y2[, duration, id])
- RootAutomator.press(x, y, duration[, id])
- RootAutomator.longPress(x, y[\, id])
-- RootAutomator.touchDown(x, y[\, id])
-- RootAutomator.touchMove(x, y[\, id])
+- RootAutomator.touchDown(x, y[, id])
+- RootAutomator.touchMove(x, y[, id])
- RootAutomator.touchUp([id])
@@ -101,9 +104,29 @@
- Android7.0以上点按与手势模拟#
-Stability: 2 - Stable本章节介绍了一些适用于Android7.0以上、不需要root权限、依赖于无障碍服务的点按与手势模拟的全局函数。
-注意以下命令只有Android7.0及以上才有效
+ 基于坐标的触摸模拟#
+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)#
+设置脚本坐标点击所适合的屏幕宽高。如果脚本运行时,屏幕宽度不一致会自动放缩坐标。
+例如在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 <number> 要点击的坐标的x值
@@ -111,16 +134,13 @@
模拟点击坐标(x, y),并返回是否点击成功。只有在点击执行完成后脚本才继续执行。
一般而言,只有点击过程(大约150毫秒)中被其他事件中断(例如用户自行点击)才会点击失败。
-使用该函数模拟连续点击时可能有点击速度过慢的问题,这时可以用[press][]函数代替。
-
-可以在开发者选项中启用指针位置来查看坐标
-
+使用该函数模拟连续点击时可能有点击速度过慢的问题,这时可以用press()函数代替。
longClick(x, y)#
模拟长按坐标(x, y), 并 返回是否成功。只有在长按执行完成(大约600毫秒)时脚本才会继续执行。
+模拟长按坐标(x, y), 并返回是否成功。只有在长按执行完成(大约600毫秒)时脚本才会继续执行。
一般而言,只有长按过程中被其他事件中断(例如用户自行点击)才会长按失败。
press(x, y, duration)#
@@ -131,7 +151,13 @@
模拟按住坐标(x, y), 并返回是否成功。只有按住操作执行完成时脚本才会继续执行。
如果按住时间过短,那么会被系统认为是点击;如果时长超过500毫秒,则认为是长按。
一般而言,只有按住过程中被其他事件中断才会操作失败。
-swipe(x1, y1, x2, y2, duration)#
+一个连点器的例子如下:
+//循环100次
+for(var i = 0; i < 100; i++){
+ //点击位置(500, 1000), 每次用时1毫秒
+ press(500, 1000, 1);
+}
+
swipe(x1, y1, x2, y2, duration)#
gestures([0, 500, [800, 300], [500, 1000]],
[0, 500, [300, 1500], [500, 1000]]);
-
setScreenMetrics(width, height)#
-设置脚本坐标点击所适合的屏幕宽高。如果脚本运行时,屏幕宽度不一致会自动放缩坐标。
-例如在1920*1080的设备中,某个操作的代码为
-setScreenMetrics(1080, 1920);
-click(800, 200);
-longClick(300, 500);
-
那么在其他设备上AutoJs会自动放缩坐标以便脚本仍然有效。
-RootAutomator#
+RootAutomator#
Stability: 2 - StableRootAutomator是一个使用root权限来模拟触摸的对象,用它可以完成触摸与多点触摸,并且这些动作的执行没有延迟。
-注意以下命令需要root权限
+一个脚本中最好只存在一个RootAutomator,并且保证脚本结束退出他。可以在exit事件中退出RootAutomator,例如:
var ra = new RootAutomator();
-
RootAutomator.tap(x, y[, id])#
+events.on('exit', function(){
+ ra.exit();
+});
+//执行一些点击操作
+...
+注意以下命令需要root权限
+RootAutomator.tap(x, y[, id])#
x <number> 横坐标
y <number> 纵坐标
@@ -182,6 +203,7 @@ ra.tap(200, 200, 2);
ra.exit();
如果不需要多点触摸,则不需要id这个参数。
多点触摸通常用于手势或游戏操作,例如模拟双指捏合、双指上滑等。
+某些情况下可能存在tap点击无反应的情况,这时可以用RootAutomator.press()函数代替。
RootAutomator.swipe(x1, x2, y1, y2[, duration, id])#
x1 <number> 滑动起点横坐标
@@ -194,43 +216,42 @@ ra.exit();
模拟一次从(x1, y1)到(x2, y2)的时间为duration毫秒的滑动。
RootAutomator.press(x, y, duration[, id])#
模拟按下位置(x, y),时长为duration毫秒。
-使用该函数模拟连续点击时可能有点击速度过慢的问题,这时可以用[RootAutomator.press][]函数代替。
RootAutomator.longPress(x, y[\, id])#
模拟长按位置(x, y)。
以上为简单模拟触摸操作的函数。如果要模拟一些复杂的手势,需要更底层的函数。
-RootAutomator.touchDown(x, y[\, id])#
+RootAutomator.touchDown(x, y[, id])#
模拟手指按下位置(x, y)。
-RootAutomator.touchMove(x, y[\, id])#
+RootAutomator.touchMove(x, y[, id])#
模拟移动手指到位置(x, y)。
RootAutomator.touchUp([id])#
模拟手指弹起。
使用root权限点击和滑动的简单命令#
-Stability: 1 - Experimental 注意:本章节的函数在后续版本很可能有改动!请勿过分依赖本章节函数的副作用。推荐使用[RootAutomator][]代替本章节的触摸函数。
+Stability: 1 - Experimental 注意:本章节的函数在后续版本很可能有改动!请勿过分依赖本章节函数的副作用。推荐使用RootAutomator代替本章节的触摸函数。
以下函数均需要root权限,可以实现任意位置的点击、滑动等。
- 这些函数通常首字母大写以表示其特殊的权限。
diff --git a/app/src/main/assets/docs/device.html b/app/src/main/assets/docs/device.html
index 0eaaf397..6ca66cd7 100644
--- a/app/src/main/assets/docs/device.html
+++ b/app/src/main/assets/docs/device.html
@@ -119,17 +119,17 @@
Device#
Stability: 2 - Stabledevice模块提供了与设备有关的信息与操作,例如获取设备宽高,内存使用率,IMEI,调整设备亮度、音量等。
-此模块的部分函数,例如调整音量,需要"修改系统设置"的权限。如果没有该权限,会抛出异常并跳转到权限设置界面。
+此模块的部分函数,例如调整音量,需要"修改系统设置"的权限。如果没有该权限,会抛出SecurityException并跳转到权限设置界面。
device.width#
-设备宽度。例如1080。
+设备屏幕分辨率宽度。例如1080。
device.height#
-设备高度。例如1920。
+
设备屏幕分辨率高度。例如1920。
device.buildId#
- <string>
diff --git a/app/src/main/assets/docs/dialogs.html b/app/src/main/assets/docs/dialogs.html
index 14794862..8ff07e79 100644
--- a/app/src/main/assets/docs/dialogs.html
+++ b/app/src/main/assets/docs/dialogs.html
@@ -72,14 +72,14 @@
目录
- Dialogs
-- dialogs.rawInput(title[, default])
-- dialogs.input(title[, default])
-- dialogs.prompt(title[, default])
-- dialogs.alert(title[, content])
-- dialogs.confirm(title[, content])
-- dialogs.select(title, items)
-- dialogs.singleChoice(title, items[, index])
-- dialogs.multiChoice(title, items[, indexes])
+- dialogs.alert(title[, content, callback])
+- dialogs.confirm(title[, content, callback])
+- dialogs.rawInput(title[, prefill, callback])
+- dialogs.input(title[, prefill, callback])
+- dialogs.prompt(title[, prefill, callback])
+- dialogs.select(title, items, callback)
+- dialogs.singleChoice(title, items[, index, callback])
+- dialogs.multiChoice(title, items[, indices, callback])
@@ -88,56 +88,128 @@
Dialogs#
-Stability: 2 - Stabledialogs 模块允许用户通过对话框与脚本进行交互。
-dialogs.rawInput(title[, default])#
-显示一个包含输入框的对话框。如果指定了 default ,那么对话框显示时输入框的内容就为该值。
-该函数也可通过 rawInput(title[, default]) 、 prompt(title[, default]) 或者 dialogs.prompt(title[, default]) 来调用。调用时脚本将阻塞直至对话框被关闭。如果用户输入内容并点击确定,函数将返回输入的内容;否则返回 null 。
-dialogs.input(title[, default])#
-等效于 eval(dialogs.rawInput(title, default))
-dialogs.prompt(title[, default])#
-见 dialogs.rawInput
-dialogs.alert(title[, content])#
+Stability: 2 - Stabledialogs 模块提供了简单的对话框支持,可以通过对话框和用户进行交互。最简单的例子如下:
+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(title[, content]) 来调用。调用时脚本将阻塞直至对话框被关闭。该函数无返回值。
-dialogs.confirm(title[, content])#
+显示一个只包含“确定”按钮的提示对话框。直至用户点击确定脚本才继续运行。
+该函数也可以作为全局函数使用。
+
alert("出现错误~", "出现未知错误,请联系脚本作者”);
+
在ui模式下该函数返回一个Promise。例如:
+"ui";
+alert("嘿嘿嘿").then(()=>{
+ //当点击确定后会执行这里
+});
+
dialogs.confirm(title[, content, callback])#
title <string> 对话框的标题。
content <string> 可选,对话框的内容。默认为空。
+callback <Function> 回调函数,可选。当用户点击确定时被调用,一般用于ui模式。
-显示一个包含“确定”和“取消”按钮的提示对话框。
-该函数也可通过 confirm(title[, content]) 来调用。调用时脚本将阻塞直至对话框被关闭。如果用户点击“确定”则返回 true ,否则返回 false 。
-dialogs.select(title, items)#
+显示一个包含“确定”和“取消”按钮的提示对话框。如果用户点击“确定”则返回 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模式。
-显示一个带有选项列表的对话框。
-该函数也可通过 dialogs.select(title, ...items) 来调用。
例如: dialogs.select("标题", ["A", "B", "C"]) 可以用 dialogs.select("标题", "A", "B", "C") 替代。
-调用时脚本将阻塞直至对话框被关闭。如果用户点击了对话框中的某个选项,该函数会返回该选项的位置(选中第一个选项返回0,第二个选项返回1,以此类推),否则返回-1。
-dialogs.singleChoice(title, items[, index])#
+显示一个带有选项列表的对话框,等待用户选择,返回用户选择的选项索引(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。
+index <number> 对话框的初始选项的位置,默认为0。
+callback <Function> 回调函数,可选。当用户点击确定时被调用,一般用于ui模式。
-显示一个带有单选框选项列表的对话框。
-调用时脚本将阻塞直至对话框被关闭。如果用户选中了对话框中的某个选项并点击“确定”,该函数会返回该选项的位置(选中第一个选项返回0,第二个选项返回1,以此类推),否则返回-1。
-dialogs.multiChoice(title, items[, indexes])#
+显示一个单选列表对话框,等待用户选择,返回用户选择的选项索引(0 ~ item.length - 1)。如果用户取消了选择,返回-1。
+在ui模式下该函数返回一个Promise。
+dialogs.multiChoice(title, items[, indices, callback])#
title <string> 对话框的标题。
items <Array> 对话框的选项列表,是一个字符串数组。
-indexes <Array> 可选,对话框的默认选项的位置数组。默认为空数组。
+indices <Array> 选项列表中初始选中的项目索引的数组,默认为空数组。
+callback <Function> 回调函数,可选。当用户点击确定时被调用,一般用于ui模式。
-显示一个带有多选框选项列表的对话框。
-调用时脚本将阻塞直至对话框被关闭。如果用户点击“确定”按钮,该函数会返回所有已选选项的位置组成的数组,否则返回空数组。
+显示一个多选列表对话框,等待用户选择,返回用户选择的选项索引的数组。如果用户取消了选择,返回[]。
+在ui模式下该函数返回一个Promise。
diff --git a/app/src/main/assets/docs/engines.html b/app/src/main/assets/docs/engines.html
index 71e54830..36bc4b64 100644
--- a/app/src/main/assets/docs/engines.html
+++ b/app/src/main/assets/docs/engines.html
@@ -83,12 +83,11 @@
ScriptExecution
ScriptEngine
ScriptConfig
@@ -98,99 +97,126 @@
- getPath()
-ScriptSource
-
Engines#
-Stability: 2 - Stableengines模块包含了一些与脚本引擎有关的函数,包括运行其他脚本,关闭脚本等。
-engines.execScript(name, script[, config])#
+Stability: 2 - Stableengines模块包含了一些与脚本环境、脚本运行、脚本引擎有关的函数,包括运行其他脚本,关闭脚本等。
+例如,获取脚本所在目录:
+toast(engines.myEngine().cwd());
+
engines.execScript(name, script[, config])#
在新线程中运行脚本script。返回一个ScriptExectuion对象。
-engines.execScriptFile(path[, config])#
+在新的脚本环境中运行脚本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 \
在新线程中运行脚本文件path。返回一个ScriptExecution对象。
-engines.execAutoFile(path[, config])#
+在新的脚本环境中运行脚本文件path。返回一个ScriptExecution对象。
+engines.execScriptFile("/sdcard/脚本/1.js");
+
engines.execAutoFile(path[, config])#
path <string> 要运行的录制文件路径。
-config \
在新线程中运行录制文件path。返回一个ScriptExecution对象。
-engines.stopAll()#
+在新的脚本环境中运行录制文件path。返回一个ScriptExecution对象。
+engines.execAutoFile("/sdcard/脚本/1.auto");
+
engines.stopAll()#
停止所有正在运行的脚本。包括当前脚本自身。
engines.stopAllAndToast()#
停止所有正在运行的脚本并显示停止的脚本数量。包括当前脚本自身。
engines.myEngine()#
返回当前脚本的脚本引擎对象(ScriptEngine)
ScriptExecution#
-执行脚本时返回的对象,可以通过他获取执行的引擎、配置、源码等。
+执行脚本时返回的对象,可以通过他获取执行的引擎、配置等,也可以停止这个执行。
+要停止这个脚本的执行,使用exectuion.getEngine().forceStop().
ScriptExecution.getEngine()#
返回执行该脚本的脚本引擎对象(ScriptEngine)
ScriptExecution.getConfig()#
返回该脚本的运行配置(ScriptConfig)
-ScriptExecution.getSource()#
-返回该脚本的源码对象(ScriptSource)
ScriptEngine#
+脚本引擎对象。
ScriptEngine.forceStop()#
停止脚本引擎的执行。
-ScriptEngine.getTag(tagName)#
+ScriptEngine.cwd()#
返回对应于tagName的附加在该脚本引擎上的额外信息。tagName包括:
-
-source 该脚本引擎当前正在执行的源码(ScriptSource)
-execute_path 该脚本引擎当前执行的路径
-
-停止脚本引擎的执行。
+返回脚本执行的路径。对于一个脚本文件而言为这个脚本所在的文件夹;对于其他脚本,例如字符串脚本,则为null或者执行时的设置值。
ScriptConfig#
脚本执行时的配置。
delay#
-延迟执行的毫秒数
+
+- <number>
+
+延迟执行的毫秒数
interval#
-循环运行时两次运行之间的时间间隔
+
+- <number>
+
+循环运行时两次运行之间的时间间隔
loopTimes#
-循环运行次数
+
+- <number>
+
+循环运行次数
getPath()#
-返回一个字符串数组表示脚本运行时模块寻找的路径。
-ScriptSource#
-脚本执行时的源码对象。可以是字符串源码、文件源码等。
-如果该源码是文件脚本,则可以通过toString()得到该文件的路径。
-getName()#
-返回该源码的名称。
-getEngineName()#
-返回执行该源码的脚本引擎的名称。
+
+- 返回 <Array>
+
+返回一个字符串数组表示脚本运行时模块寻找的路径。
diff --git a/app/src/main/assets/docs/events.html b/app/src/main/assets/docs/events.html
index 84100e7a..848d715e 100644
--- a/app/src/main/assets/docs/events.html
+++ b/app/src/main/assets/docs/events.html
@@ -89,11 +89,31 @@
事件: 'key_down'
事件: 'key_up'
事件: 'exit`
-obverseNotification()
+events.observeNotification()
+events.observeToast()
事件: 'toast'
事件: 'notification'
+Notification
+
+KeyEvent
+
+keys
EventEmitter
-KeyEvent
-
-Keys
@@ -128,18 +139,28 @@
Events#
Stability: 2 - Stableevents模块提供了监听手机通知、按键、触摸的接口。您可以用他配合自动操作函数完成自动化工作。
events本身是一个EventEmiiter, 但内置了一些事件、包括按键事件、通知事件、Toast事件等。
-events.emitter()#
-返回一个新的[EventEmitter][]。这个EventEmitter没有内置任何事件。
+需要注意的是,事件的处理是单线程的,并且仍然在原线程执行,如果脚本主体或者其他事件处理中有耗时操作、轮询等,则事件将无法得到及时处理(会进入事件队列等待脚本主体或其他事件处理完成才执行)。例如:
+auto();
+events.observeNotification();
+events.on('toast', function(t){
+ //这段代码将得不到执行
+ log(t);
+});
+while(true){
+ //死循环
+}
+
events.emitter()#
+返回一个新的EventEmitter。这个EventEmitter没有内置任何事件。
events.observeKey()#
-启用按键监听,例如音量键、Home键。此函数使用无障碍服务实现,因此此函数会调用auto()确保无障碍服务启用。
-只有这个函数成功执行后, [onKeyDown][], [onKeyUp][]等按键事件的监听才有效。
+启用按键监听,例如音量键、Home键。按键监听使用无障碍服务实现,如果无障碍服务未启用会抛出异常并提示开启。
+只有这个函数成功执行后, onKeyDown, onKeyUp等按键事件的监听才有效。
该函数在安卓4.3以上才能使用。
events.onKeyDown(keyName, listener)#
keyName <string> 要监听的按键名称
listener <Function> 按键监听器。参数为一个KeyEvent。
-注册一个按键监听函数,当有keyName对应的按键被按下会调用该函数。可用的按键名称参见[Keys][]。
+注册一个按键监听函数,当有keyName对应的按键被按下会调用该函数。可用的按键名称参见Keys。
例如:
//启用按键监听
events.observeKey();
@@ -198,7 +219,7 @@ events.onKeyDown("home", function(event){
events.observeTouch()#
启用屏幕触摸监听。(需要root权限)
只有这个函数被成功执行后, 触摸事件的监听才有效。
-没有root权限调用该函数则什么也不会发生。(注意: 这个行为未来可能会更改为抛出异常)
+没有root权限调用该函数则什么也不会发生。
events.setTouchEventTimeout(timeout)#
timeout <number> 两个触摸事件的最小间隔。单位毫秒。默认为10毫秒。如果number小于0,视为0处理。
@@ -210,7 +231,7 @@ events.onKeyDown("home", function(event){
返回触摸事件的最小时间间隔。
events.onTouch(listener)#
-listener <Function> 参数为[Point][]的函数
+listener <Function> 参数为Point的函数
注册一个触摸监听函数。相当于on("touch", listener)。
例如:
@@ -237,13 +258,21 @@ events.on("key", function(keyCode, event){
});
其中监听器的参数KeyCode包括:
-KeyEvent.KEYCODE_HOME 主页键
-KeyEvent.KEYCODE_BACK 返回键
-KeyEvent.KEYCODE_MENU 菜单键
-KeyEvent.KEYCODE_VOLUMEUP 音量上键
-KeyEvent.KEYCODE_VOLUMEDOWN 音量下键
+keys.home 主页键
+keys.back 返回键
+keys.menu 菜单键
+keys.volume_up 音量上键
+keys.volume_down 音量下键
-事件: 'key_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> 事件
@@ -274,18 +303,18 @@ events.on("exit", function(){
log("结束运行");
});
log("即将结束运行");
-obverseNotification()#
-开启通知(包括Toast)监听。
-通知与Toast监听依赖于无障碍服务,因此这个函数会调用auto()来确保无障碍服务启用。
+events.observeNotification()#
+开启通知监听。例如QQ消息、微信消息、推送等通知。
+通知监听依赖于通知服务,如果通知服务没有运行,会抛出异常并跳转到通知权限开启界面。(有时即使通知权限已经开启通知服务也没有运行,这时需要关闭权限再重新开启一次)
例如:
events.obverseNotification();
events.onNotification(function(notification){
log(notification.getText());
});
-events.onToast(function(toast){
- log(toast.getText());
-});
-
事件: 'toast'#
+events.observeToast()#
+开启Toast监听。
+Toast监听依赖于无障碍服务,因此此函数会确保无障碍服务运行。
+事件: 'toast'#
toast <Object>
getText() 获取Toast的文本内容
@@ -293,23 +322,91 @@ events.onToast(function(toast){
-当有应用发出toast(气泡消息)时会触发该事件。但Auto.js软件本身的toast除外。
-例如,要记录发出所有toast的应用:
-events.obverseNotification();
+
当有应用发出toast(气泡消息)时会触发该事件。但Auto.js软件本身的toast除外。
+例如,要记录发出所有toast的应用:
+events.observeToast();
events.onToast(function(toast){
log("Toast内容: " + toast.getText() + " 包名: " + toast.getPackageName());
});
事件: 'notification'#
-notification <Object> 通知
+notification Notification 通知对象
-当有应用发出通知时会触发该事件。
+当有应用发出通知时会触发该事件,参数为Notification。
例如:
events.observeNotification();
-events.on("notification", function(notification){
- log(notification);
+events.on("notification", function(n){
+ log("收到新通知:\n 标题: %s, 内容: %s, \n包名: %s", n.getTitle(), n.getText(), n.getPackageName());
});
-
注意: 这是一个实验性功能。实测只有某些情况下的通知才能被正确捕捉。
+
Notification#
+通知对象,可以获取通知详情,包括通知标题、内容、发出通知的包名、时间等,也可以对通知进行操作,比如点击、删除。
+Notification.number#
+
+- <number>
+
+通知数量。例如QQ连续收到两条消息时number为2。
+Notification.when#
+
+- <number>
+
+通知发出时间的时间戳,可以用于构造Date对象。例如:
+events.observeNotification();
+events.on("notification", function(n){
+ log("通知时间为}" + new Date(n.when));
+});
+
Notification.getPackageName()#
+
+- 返回 <string>
+
+获取发出通知的应用包名。
+Notification.getTitle()#
+
+- 返回 <string>
+
+获取通知的标题。
+Notification.getText()#
+
+- 返回 <string>
+
+获取通知的内容。
+Notification.click()#
+点击该通知。例如对于一条QQ消息,点击会进入具体的聊天界面。
+Notification.delete()#
+删除该通知。该通知将从通知栏中消失。
+KeyEvent#
+Stability: 2 - StableKeyEvent.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()#
+
+- 返回 <number>
+
+返回事件发生的时间戳。
+KeyEvent.getDownTime()#
+返回最近一次按下事件的时间戳。如果本身是按下事件,则与getEventTime()相同。
+KeyEvent.keyCodeToString(keyCode)#
+把键值转换为字符串。例如KEYCODE_HOME转换为"KEYCODE_HOME"。
+keys#
+Stability: 2 - Stable按键事件中所有可用的按键名称为:
+
+volume_up 音量上键
+volume_down 音量下键
+home 主屏幕键
+back 返回键
+menu 菜单键
+
EventEmitter#
Stability: 2 - StableEventEmitter.defaultMaxListeners#
每个事件默认可以注册最多 10 个监听器。 单个 EventEmitter 实例的限制可以使用 emitter.setMaxListeners(n) 方法改变。 所有 EventEmitter 实例的默认值可以使用 EventEmitter.defaultMaxListeners 属性改变。
@@ -473,37 +570,6 @@ myEmitter.emit('event');
默认情况下,如果为特定事件添加了超过 10 个监听器,则 EventEmitter 会打印一个警告。 此限制有助于寻找内存泄露。 但是,并不是所有的事件都要被限为 10 个。 emitter.setMaxListeners() 方法允许修改指定的 EventEmitter 实例的限制。 值设为 Infinity(或 0)表明不限制监听器的数量。
返回一个 EventEmitter 引用,可以链式调用。
-KeyEvent#
-Stability: 2 - StableKeyEvent.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()#
-返回事件发生的时间戳。返回值的类型是number。
-KeyEvent.getDownTime()#
-返回最近一次按下事件的时间戳。如果本身是按下事件,则与getEventTime()相同。
-KeyEvent.keyCodeToString(keyCode)#
-把键值转换为字符串。例如KEYCODE_HOME转换为"KEYCODE_HOME"。
-Keys#
-Stability: 2 - Stable按键事件中所有可用的按键名称为:
-
-volume_up 音量上键
-volume_down 音量下键
-home 主屏幕键
-back 返回键
-menu 菜单键
-
diff --git a/app/src/main/assets/docs/files.html b/app/src/main/assets/docs/files.html
index 4af3bdf3..2ae733ad 100644
--- a/app/src/main/assets/docs/files.html
+++ b/app/src/main/assets/docs/files.html
@@ -77,7 +77,7 @@
files.isEmptyDir(path)
files.join(parent, child)
files.create(path)
-files.createIfNotExists(path)
+files.createWithDirs(path)
files.exists(path)
files.ensureDir(path)
files.read(path[, encoding = "utf-8"])
@@ -96,6 +96,7 @@
files.remove(path)
files.removeDir(path)
files.getSdcardPath()
+files.cwd()
files.listDir(path[, filter])
open(path[, mode = "r", encoding = "utf-8", bufferSize = 8192])
@@ -123,68 +124,97 @@
Files#
Stability: 2 - Stablefiles模块提供了一些常见的文件处理,包括文件读写、移动、复制、删掉等。
+一次性的文件读写可以直接使用files.read(), files.write(), files.append()等方便的函数,但如果需要频繁读写或随机读写,则使用open()函数打开一个文件对象来操作文件,并在操作完毕后调用close()函数关闭文件。
files.isFile(path)#
返回路径path是否是文件。
-files.isDir(path)#
+log(files.isDir("/sdcard/文件夹/")); //返回false
+log(files.isDir("/sdcard/文件.txt")); //返回true
+
files.isDir(path)#
返回路径path是否是文件夹。
-files.isEmptyDir(path)#
+log(files.isDir("/sdcard/文件夹/")); //返回true
+log(files.isDir("/sdcard/文件.txt")); //返回false
+
files.isEmptyDir(path)#
返回文件夹path是否为空文件夹。如果该路径并非文件夹,则直接返回false。
+返回文件夹path是否为空文件夹。如果该路径并非文件夹,则直接返回false。
files.join(parent, child)#
连接两个路径并返回,例如files.join("/sdcard/", "1.txt")返回"/sdcard/1.txt"。
files.create(path)#
创建一个文件并返回是否创建成功。
-files.createIfNotExists(path)#
+创建一个文件或文件夹并返回是否创建成功。如果文件已经存在,则直接返回false。
+files.create("/sdcard/新文件夹/");
+
files.createWithDirs(path)#
创建一个文件并返回是否创建成功。
-files.exists(path)#
+创建一个文件或文件夹并返回是否创建成功。如果文件所在文件夹不存在,则先创建他所在的一系列文件夹。如果文件已经存在,则直接返回false。
+files.createWithDirs("/sdcard/新文件夹/新文件夹/新文件夹/1.txt");
+
files.exists(path)#
返回在路径path处的文件是否存在。
files.ensureDir(path)#
path <string> 路径
-确保路径path所在的文件夹存在。
+确保路径path所在的文件夹存在。如果该路径所在文件夹不存在,则创建该文件夹。
例如对于路径"/sdcard/Download/ABC/1.txt",如果/Download/文件夹不存在,则会先创建Download,再创建ABC文件夹。
files.read(path[, encoding = "utf-8"])#
读取文本文件path的所有内容并返回一个字符串。
-files.readBytes(path)#
+读取文本文件path的所有内容并返回。如果文件不存在,则抛出FileNotFoundException。
+log(files.read("/sdcard/1.txt"));
+
files.readBytes(path)#
path <string> 路径
+- 返回 <byte[]>
-读取文件path的所有内容并返回一个字节数组。
-注意,该数组是Java的数组,不具有JavaScript数组的函数。
-files.write(path, text[, encoding = "utf-8"])#
+读取文件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中。如果文件存在则覆盖,不存在则创建。
-files.writeBytes(path, bytes)#
+var text = "文件内容";
+//写入文件
+files.write("/sdcard/1.txt", text);
+//用其他应用查看文件
+app.viewFile("/sdcard/1.txt");
+
files.writeBytes(path, bytes)#
把text追加到文件path的末尾。如果文件不存在则创建。
-files.appendBytes(path, text[, encoding = 'utf-8'])#
+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[]> 字节数组,要写入的二进制数据
@@ -207,75 +242,99 @@
复制文件。例如files.copy("/sdcard/1.txt", "/sdcard/Download/1.txt")。
+
复制文件,返回是否复制成功。例如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.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卡,即外部存储器。
+
+- 返回 <string>
+
+返回SD卡路径。所谓SD卡,即外部存储器。
+files.cwd()#
+
+- 返回 <string>
+
+返回脚本的"当前工作文件夹路径"。该路径指的是,如果脚本本身为脚本文件,则返回这个脚本文件所在目录;否则返回null获取其他设定路径。
+例如,对于脚本文件"/sdcard/脚本/1.js"运行files.cwd()返回"/sdcard/脚本/"。
files.listDir(path[, filter])#
path <string> 路径
-filter <Function> 过滤函数,可选。接收一个String参数(文件名),返回一个Boolean值。
+filter <Function> 过滤函数,可选。接收一个string参数(文件名),返回一个boolean值。
列出文件夹path下的满足条件的文件和文件夹的名称的数组。如果不加filter参数,则返回所有文件和文件夹。
-例如,获取sdcard目录下的txt文件为
-var txtFiles = files.listDir("/sdcard/", function(name){
- return name.endsWith(".txt") && files.isFile("/sdcard/" + name);
+列出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": 附加模式。该模式下将会把写入的文本附加到文件末尾。
目前暂不支持二进制模式,随机读写模式。
+- "r": 只读文本模式。该模式下只能对文件执行文本读取操作。
+- "w": 只写文本模式。该模式下只能对文件执行文本覆盖写入操作。
+- "a": 附加文本模式。该模式下将会把写入的文本附加到文件末尾。
+- "rw": 随机读写文本模式。该模式下将会把写入的文本附加到文件末尾。
目前暂不支持二进制模式,随机读写模式。
encoding <string> 字符编码。
-bufferSize <Number> 文件读写的缓冲区大小。
+bufferSize <number> 文件读写的缓冲区大小。
打开一个文件。根据打开模式返回不同的文件对象。包括:
diff --git a/app/src/main/assets/docs/globals.html b/app/src/main/assets/docs/globals.html
index e7260417..2ba8641e 100644
--- a/app/src/main/assets/docs/globals.html
+++ b/app/src/main/assets/docs/globals.html
@@ -72,14 +72,9 @@
目录
- 全局变量与函数
-- sleep([n])
-- launchPackage(packageName)
-- launchApp(appName)
+- sleep(n)
- currentPackage()
- currentActivity()
-- getPackageName(appName)
-- getAppName(packageName)
-- openAppSetting(packageName)
- setClip(text)
- getClip()
- toast(message)
@@ -98,64 +93,51 @@
全局变量与函数#
-全局变量和函数在所有模块中均可使用。 但以下变量的作用域只在模块内,详见 module文档:
+全局变量和函数在所有模块中均可使用。 但以下变量的作用域只在模块内,详见 module:
- exports
- module
- require()
以下的对象是特定于 Auto.js 的。 有些内置对象是 JavaScript 语言本身的一部分,它们也是全局的。
-一些模块中的函数为了使用方便也可以直接全局使用,这些函数在此不再赘述。例如timers模块的setInterval等函数。
-sleep([n])#
+一些模块中的函数为了使用方便也可以直接全局使用,这些函数在此不再赘述。例如timers模块的setInterval, setTimeout等函数。
+sleep(n)#
n <number> 毫秒数
暂停运行n毫秒的时间。1秒等于1000毫秒。
-launchPackage(packageName)#
-
-packageName <string> 应用包名
-
-运行包名为packageName的应用主界面(Launcher)。例如,打开微信为:
-launchPackage("com.tencent.mm");
-
如果存在多个应用包名相同的情况(如双开应用),如何处理取决于操作系统。在MIUI中会弹出多开应用的选择界面。
-launchApp(appName)#
-
-appName <String> 应用名称
-
-运行应用名称为appName的应用主界面。当有应用名称相同时只运行其中某一个。
例如,打开微信为:
-launchApp("微信");
+//暂停5毫秒
+sleep(5000);
currentPackage()#
-返回最近一次监测到的正在运行的应用的包名,一般可以认为就是当前正在运行的应用的包名。
+
+- 返回 <string>
+
+返回最近一次监测到的正在运行的应用的包名,一般可以认为就是当前正在运行的应用的包名。
+此函数依赖于无障碍服务,如果服务未启动,则抛出异常并提示用户启动。
currentActivity()#
-返回最近一次监测到的正在运行的Activity的名称,一般可以认为就是当前正在运行的Activity的名称。
-getPackageName(appName)#
获取应用的包名。例如getPackageName("QQ")为"com.tencent.mobileqq"。如果有相同名称的应用,只返回其中某一个的包名。如果不存在这个名称的应用,会返回null。
-getAppName(packageName)#
-
-packageName <string> 应用包名
-
-返回对应包名的应用的名称。如果应用不存在,返回null。
-openAppSetting(packageName)#
-
-packageName <string> 应用包名
-
-打开某个应用的应用详情页,也就是管理应用权限和可以停止其运行的页面。如果应用包名不存在,则返回false;否则返回true。
+
返回最近一次监测到的正在运行的Activity的名称,一般可以认为就是当前正在运行的Activity的名称。
+此函数依赖于无障碍服务,如果服务未启动,则抛出异常并提示用户启动。
setClip(text)#
text <string> 文本
设置剪贴板内容。此剪贴板即系统剪贴板,在一般应用的输入框中"粘贴"既可使用。
-getClip()#
-返回系统剪贴板的内容。
-toast(message)#
+
setClip("剪贴板文本");
+
getClip()#
-- message <string>> | <{Object> 要显示的信息
+- 返回 <string>
+
+返回系统剪贴板的内容。
+toast("剪贴板内容为:" + getClip());
+
toast(message)#
+
+- message <string> 要显示的信息
以气泡显示信息message几秒。(具体时间取决于安卓系统,一般都是2秒)
-注意,信息的显示是"异步"执行的(不属于Looper循环),并且,不会等待信息消失程序才继续执行。如果在循环中执行该命令,可能出现脚本停止运行后仍然有不断的气泡信息出现的情况。
+
注意,信息的显示是"异步"执行的,并且,不会等待信息消失程序才继续执行。如果在循环中执行该命令,可能出现脚本停止运行后仍然有不断的气泡信息出现的情况。
例如:
for(var i = 0; i < 100; i++){
toast(i);
@@ -174,11 +156,10 @@ toast = function(message){
}
for(var i = 0; i < 100; i++){
toast(i);
- sleep(2000);
}
toastLog(message)#
-- message \
| \
+- message <string> 要显示的信息
相当于toast(message);log(message)。显示信息message并在控制台中输出。参见console.log。
waitForActivity(activity[, period = 200])#
@@ -195,14 +176,19 @@ for(var i = 0; i < 100; i++){
等待指定的应用出现。例如waitForPackage("com.tencent.mm")为等待当前界面为微信。
exit()#
立即停止脚本运行。
+立即停止是通过抛出ScriptInterrupttedException来实现的,因此如果用try...catch把exit()函数的异常捕捉,则脚本不会立即停止,仍会运行几行后再停止。
random(min, max)#
返回一个在[min...max]之间的随机数。例如random(0, 2)可能产生0, 1, 2.
+返回一个在[min...max]之间的随机数。例如random(0, 2)可能产生0, 1, 2。
random()#
-返回在[0, 1)的随机浮点数。
+
+- 返回 <number>
+
+返回在[0, 1)的随机浮点数。
context#
全局变量。一个android.content.Context对象。
注意该对象为ApplicationContext,因此不能用于界面、对话框等的创建。
diff --git a/app/src/main/assets/docs/keys.html b/app/src/main/assets/docs/keys.html
index c39e13c2..de335ada 100644
--- a/app/src/main/assets/docs/keys.html
+++ b/app/src/main/assets/docs/keys.html
@@ -103,27 +103,49 @@
Keys#
按键模拟部分提供了一些模拟物理按键的全局函数,包括Home、音量键、照相键等,有的函数依赖于无障碍服务,有的函数依赖于root权限。
+一般来说,以大写字母开头的函数都依赖于root权限。执行此类函数时,如果没有root权限,则函数执行后没有效果,并会在控制台输出一个警告。
back()#
-模拟按下返回键。返回是否执行成功。
+
+- 返回 <boolean>
+
+模拟按下返回键。返回是否执行成功。
此函数依赖于无障碍服务。
home()#
-模拟按下Home键。返回是否执行成功。
+
+- 返回 <boolean>
+
+模拟按下Home键。返回是否执行成功。
此函数依赖于无障碍服务。
powerDialog()#
-弹出电源键菜单。返回是否执行成功。
+
+- 返回 <boolean>
+
+弹出电源键菜单。返回是否执行成功。
此函数依赖于无障碍服务。
notifications()#
-弹出通知栏。返回是否执行成功。
+
+- 返回 <boolean>
+
+拉出通知栏。返回是否执行成功。
此函数依赖于无障碍服务。
quickSettings()#
-显示快速设置(下拉通知栏到底)。返回是否执行成功。
+
+- 返回 <boolean>
+
+显示快速设置(下拉通知栏到底)。返回是否执行成功。
此函数依赖于无障碍服务。
recents()#
-显示最近任务。返回是否执行成功。
+
+- 返回 <boolean>
+
+显示最近任务。返回是否执行成功。
此函数依赖于无障碍服务。
splitScreen()#
-分屏。返回是否执行成功。
-此函数依赖于无障碍服务。
+
+- 返回 <boolean>
+
+分屏。返回是否执行成功。
+此函数依赖于无障碍服务, 并且需要系统自身功能的支持。
Home()#
模拟按下Home键。
此函数依赖于root权限。
diff --git a/app/src/main/assets/docs/modules.html b/app/src/main/assets/docs/modules.html
index 12dd7a30..fc72eada 100644
--- a/app/src/main/assets/docs/modules.html
+++ b/app/src/main/assets/docs/modules.html
@@ -80,7 +80,7 @@
module (模块)#
Stability: 2 - StableAuto.js 有一个简单的模块加载系统。 在 Auto.js 中,文件和模块是一一对应的(每个文件被视为一个独立的模块)。
例子,假设有一个名为 foo.js 的文件:
-const circle = require('./circle.js');
+const circle = require('circle.js');
console.log("半径为 4 的圆的面积是 %d", circle.area(4));
在第一行中,foo.js 加载了同一目录下的 circle.js 模块。
circle.js 文件的内容为:
@@ -94,12 +94,12 @@ circle.circumference = (r) => 2 * PI * r;
module.exports = circle;
circle.js 模块导出了 area() 和 circumference() 两个函数。 通过在特殊的 exports 对象上指定额外的属性,函数和对象可以被添加到模块的根部。
-模块内的本地变量是私有的,因为模块被 Node.js 包装在一个函数中(详见模块包装器)。 在这个例子中,变量 PI 是 circle.js 私有的。
+模块内的本地变量是私有的。 在这个例子中,变量 PI 是 circle.js 私有的,不会影响到加载他的脚本的变量环境。
module.exports属性可以被赋予一个新的值(例如函数或对象)。
如下,bar.js 会用到 square 模块,square 导出一个构造函数:
-const square = require('./square.js');
+const square = require('square.js');
const mySquare = square(2);
-console.log(`正方形的面积是 ${mySquare.area()}`);
+console.log("正方形的面积是 %d", mySquare.area());
square 模块定义在 square.js 中:
// 赋值给 `exports` 不会修改模块,必须使用 `module.exports`
diff --git a/app/src/main/assets/docs/shell.html b/app/src/main/assets/docs/shell.html
index 6013117c..77d292d8 100644
--- a/app/src/main/assets/docs/shell.html
+++ b/app/src/main/assets/docs/shell.html
@@ -157,11 +157,11 @@
- cmd <string> 要执行的命令
- root <Boolean> 是否以root权限运行,默认为false。
-
返回运行一个对象表示命令的执行结果。其属性如下:
+一次性执行命令cmd, 并返回命令的执行结果。返回对象的其属性如下:
- code <number> 返回码。执行成功时为0,失败时为非0的数字。
- result <string> 运行结果(stdout输出结果)
-- error \
运行的错误信息(stderr输出结果)。例如执行需要root权限的命令但没有授予root权限会返回错误信息"Permission denied"。
+- error <string> 运行的错误信息(stderr输出结果)。例如执行需要root权限的命令但没有授予root权限会返回错误信息"Permission denied"。
示例(强制停止微信) :
var result = shell("am force-stop com.tencent.mm", true);
@@ -180,6 +180,7 @@ if(result.code == 0){
Shell对象的"构造函数"。
var sh = new Shell(true);
+//强制停止微信
sh.exec("am force-stop com.tencent.mm");
sh.exit();
Shell.exec(cmd)#
@@ -190,7 +191,7 @@ sh.exit();
注意,命令执行是"异步"的、非阻塞的。也就是不会等待命令完成后才继续向下执行。
尽管这样的设计使用起来有很多不便之处,但受限于终端模拟器,暂时没有解决方式;如果后续能找到解决方案,则将提供Shell.execAndWaitFor函数。
Shell.exit()#
-直接退出shell。这意味着正在执行的命令会被强制退出。
+直接退出shell。正在执行的命令会被强制退出。
Shell.exitAndWaitFor()#
执行"exit"命令并等待执行命令执行完成、退出shell。
此函数会执行exit命令来正常退出shell。
@@ -200,21 +201,24 @@ sh.exit();
设置该Shell的回调函数,以便监听Shell的输出。可以包括以下属性:
-- onOutput <function> 每当shell有新的输出时便会调用该函数。其参数是一个字符串。
-- onNewLine <function> 每当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();
@@ -222,7 +226,7 @@ sh.exit();
以下关于shell命令的资料来自AndroidStudio用户指南:Shell命令。
am命令#
am命令即Activity Manager命令,用于管理应用程序活动、服务等。
-以下命令均以"am "开头,例如"shell(\"am start -p com.tencent.mm\");"(启动微信)
+以下命令均以"am "开头,例如shell('am start -p com.tencent.mm');(启动微信)
start [options] intent#
启动 intent 指定的 Activity(应用程序活动)。
请参阅 intent 参数的规范。
选项包括:
diff --git a/app/src/main/assets/docs/storages.html b/app/src/main/assets/docs/storages.html
index 3aac0edd..e68e2e6c 100644
--- a/app/src/main/assets/docs/storages.html
+++ b/app/src/main/assets/docs/storages.html
@@ -105,7 +105,7 @@ 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)#
diff --git a/app/src/main/assets/docs/threads.html b/app/src/main/assets/docs/threads.html
index 0d35f27e..55805154 100644
--- a/app/src/main/assets/docs/threads.html
+++ b/app/src/main/assets/docs/threads.html
@@ -73,31 +73,333 @@
- Threads
+- Thread
+
+- 线程安全
+- sync(func)
+
+
+- 线程通信
Threads#
-Stability: 1 - Experimentthreads模块提供了多线程支持。可以启动新线程来运行脚本。新线程会在脚本停止时也自动停止。
-但是,在新线程中暂时不能使用timers模块的函数,包括setTimeout, setInterval等。而且目前在新线程调用exit()函数时只会退出当前线程。
+Stability: 1 - Experimentthreads模块提供了多线程支持,可以启动新线程来运行脚本。
+脚本主线程会等待所有子线程执行完成后才停止执行,因此如果子线程中有死循环,请在必要的时候调用exit()来直接停止脚本或threads.shutDownAll()来停止所有子线程。
+通过threads.start()启动的所有线程会在脚本被强制停止时自动停止。
+由于JavaScript自身没有多线程的支持,因此您可能会遇到意料之外的问题。
threads.start(action)#
action <Function> 要在新线程执行的函数
+- 返回 Thread
启动一个新线程并执行action。
例如:
threads.start(function(){
+ //在新线程执行的代码
while(true){
- log("线程2");
+ log("子线程");
}
});
while(true){
- log("线程1");
+ 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])#
+
+区别在于, 该定时器会在该线程执行。如果当前线程仍未开始执行或已经执行结束,则抛出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])#
+
+区别在于, 该定时器会在该线程执行。如果当前线程仍未开始执行或已经执行结束,则抛出IllegalStateException。
+Thread.setImmediate(callback[, ...args])#
+
+区别在于, 该定时器会在该线程执行。如果当前线程仍未开始执行或已经执行结束,则抛出IllegalStateException。
+Thread.clearInterval(id)#
+
+区别在于, 该定时器会在该线程执行。如果当前线程仍未开始执行或已经执行结束,则抛出IllegalStateException。
+Thread.clearTimeout(id)#
+
+区别在于, 该定时器会在该线程执行。如果当前线程仍未开始执行或已经执行结束,则抛出IllegalStateException。
+Thread.clearImmediate(id)#
+
+区别在于, 该定时器会在该线程执行。如果当前线程仍未开始执行或已经执行结束,则抛出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。
+
diff --git a/app/src/main/assets/docs/timers.html b/app/src/main/assets/docs/timers.html
index 7d1d4955..109ea682 100644
--- a/app/src/main/assets/docs/timers.html
+++ b/app/src/main/assets/docs/timers.html
@@ -72,12 +72,12 @@
目录
@@ -88,14 +88,25 @@
Timers#
Stability: 2 - Stabletimers 模块暴露了一个全局的 API,用于在某个未来时间段调用调度函数。 因为定时器函数是全局的,所以使用该 API 无需调用 timers.*
Auto.js 中的计时器函数实现了与 Web 浏览器提供的定时器类似的 API,除了它使用了一个不同的内部实现,它是基于 Android Looper-Handler消息循环机制构建的。其实现机制与Node.js比较相似。
-setImmediate(callback[, ...args])#
-
-callback <Function> 在Looper循环的当前回合结束时要调用的函数。
-...args <any> 当调用 callback 时要传入的可选参数。
-
-预定立即执行的 callback,它是在 I/O 事件的回调之后被触发。 返回一个用于 clearImmediate() 的 id。
-当多次调用 setImmediate() 时,callback 函数会按照它们被创建的顺序依次执行。 每次事件循环迭代都会处理整个回调队列。 如果一个立即定时器是被一个正在执行的回调排入队列的,则该定时器直到下一次事件循环迭代才会被触发。
-setInterval(callback, delay[, ...args])#
+例如,要在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 之前要等待的毫秒数。
@@ -112,22 +123,38 @@
预定在 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 对象。
-clearInterval(id)#
-
-id <number> 一个 setInterval() 返回的 id。
-
-取消一个由 setInterval() 创建的 Timeout 对象。
-clearTimeout(id)#
-
-id <number> 一个 setTimeout() 返回的 id。
-
-取消一个由 setTimeout() 创建的 Timeout 对象。