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 @@

目录

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

-

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

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

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

+

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

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

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

+

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

+
Stability: 2 - Stable

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

click(x, y)#

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

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

device.buildId#

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

+

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

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

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

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

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

-

显示一个包含“确定”和“取消”按钮的提示对话框。

-

该函数也可通过 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])#

+
+

显示一个包含输入框的对话框,等待用户输入内容,并在用户点击确定时将输入的字符串返回。如果用户取消了输入,返回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)#

-

显示一个带有选项列表的对话框。

-

该函数也可通过 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])#

-

显示一个带有单选框选项列表的对话框。

-

调用时脚本将阻塞直至对话框被关闭。如果用户选中了对话框中的某个选项并点击“确定”,该函数会返回该选项的位置(选中第一个选项返回0,第二个选项返回1,以此类推),否则返回-1。

-

dialogs.multiChoice(title, items[, indexes])#

+

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

+

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

+

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

-

显示一个带有多选框选项列表的对话框。

-

调用时脚本将阻塞直至对话框被关闭。如果用户点击“确定”按钮,该函数会返回所有已选选项的位置组成的数组,否则返回空数组。 +

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

+

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

Engines#

-
Stability: 2 - Stable

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

-

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

+
Stability: 2 - Stable

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

+

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

+
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。返回一个ScriptExecution对象。

-

engines.execAutoFile(path[, config])#

+

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

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

engines.execAutoFile(path[, 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包括:

- -

停止脚本引擎的执行。

+

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

ScriptConfig#

脚本执行时的配置。

delay#

-

延迟执行的毫秒数

+
+

延迟执行的毫秒数

interval#

-

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

+
+

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

loopTimes#

-

循环运行次数

+
+

循环运行次数

getPath()#

-

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

-

ScriptSource#

-

脚本执行时的源码对象。可以是字符串源码、文件源码等。

-

如果该源码是文件脚本,则可以通过toString()得到该文件的路径。

-

getName()#

-

返回该源码的名称。

-

getEngineName()#

-

返回执行该源码的脚本引擎的名称。

+
+

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

Events#

Stability: 2 - Stable

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

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对应的按键被按下会调用该函数。可用的按键名称参见[Keys][]。

+

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

例如:

//启用按键监听
 events.observeKey();
@@ -1425,7 +1669,7 @@ events.onKeyDown("home", function(event){
 

events.observeTouch()#

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

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

-

没有root权限调用该函数则什么也不会发生。(注意: 这个行为未来可能会更改为抛出异常)

+

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

events.setTouchEventTimeout(timeout)#

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

-

事件: 'key_down'#

+

例如:

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

事件: 'key_down'#

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

+

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

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

事件: 'notification'#

-

当有应用发出通知时会触发该事件。

+

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

例如:

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

注意: 这是一个实验性功能。实测只有某些情况下的通知才能被正确捕捉。

+

Notification#

+

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

+

Notification.number#

+
+

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

+

Notification.when#

+
+

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

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

Notification.getPackageName()#

+
+

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

+

Notification.getTitle()#

+
+

获取通知的标题。

+

Notification.getText()#

+
+

获取通知的内容。

+

Notification.click()#

+

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

+

Notification.delete()#

+

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

+

KeyEvent#

+
Stability: 2 - Stable

KeyEvent.getAction()#

+

返回事件的动作。包括:

+ +

KeyEvent.getKeyCode()#

+

返回按键的键值。包括:

+ +

KeyEvent.getEventTime()#

+
+

返回事件发生的时间戳。

+

KeyEvent.getDownTime()#

+

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

+

KeyEvent.keyCodeToString(keyCode)#

+

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

+

keys#

+
Stability: 2 - Stable

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

+

EventEmitter#

Stability: 2 - Stable

EventEmitter.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 - Stable

KeyEvent.getAction()#

-

返回事件的动作。包括:

- -

KeyEvent.getKeyCode()#

-

返回按键的键值。包括:

- -

KeyEvent.getEventTime()#

-

返回事件发生的时间戳。返回值的类型是number。

-

KeyEvent.getDownTime()#

-

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

-

KeyEvent.keyCodeToString(keyCode)#

-

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

-

Keys#

-
Stability: 2 - Stable

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

-

Floaty#

@@ -1833,68 +2122,97 @@ w.exit.click(()=> w.close());

Files#

Stability: 2 - Stable

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

+

一次性的文件读写可以直接使用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所在的文件夹存在。

+

确保路径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的所有内容并返回一个字节数组。

-

注意,该数组是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'])#

复制文件,返回是否复制成功。例如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卡,即外部存储器。

+
+

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

+

files.cwd()#

+
+

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

+

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

files.listDir(path[, filter])#

列出文件夹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> 文件读写的缓冲区大小。

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

-

返回运行一个对象表示命令的执行结果。其属性如下:

+

一次性执行命令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 - Experiment

    threads模块提供了多线程支持。可以启动新线程来运行脚本。新线程会在脚本停止时也自动停止。

    -

    但是,在新线程中暂时不能使用timers模块的函数,包括setTimeout, setInterval等。而且目前在新线程调用exit()函数时只会退出当前线程。

    +
    Stability: 1 - Experiment

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

    +

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

    +

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

    +

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

    threads.start(action)#

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

+
+

返回当前线程。

+

threads.disposable()#

+
+

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

+

threads.atomic([initialValue])#

+
+

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

+

threads.lock()#

+
+

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

+

Thread#

+

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

+

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

+
var thread = threads.start(function(){
+    //在子线程执行的定时器
+    setInterval(function(){
+        log("子线程:" + threads.currentThread());
+    }, 1000);
+});
+
+log("当前线程为主线程:" + threads.currentThread());
+
+//等待子线程启动
+thread.waitFor();
+//在子线程执行的定时器
+thread.setTimeout(function(){
+    //这段代码会在子线程执行
+    log("当前线程为子线程:" + threads.currentThread());
+}, 2000);
+
+sleep(30 * 1000);
+thread.interrupt();
+

Thread.interrupt()#

+

中断线程运行。

+

Thread.join([timeout])#

+
+

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

+

例如:

+
var sum = 0;
+//启动子线程计算1加到10000
+var thread = threads.start(function(){
+    for(var i = 0; i < 10000; i++){
+        sum += i;
+    }
+});
+//等待该线程完成
+thread.join();
+toast("sum = " + sum);
+

isAlive()#

+
+

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

+

waitFor()#

+

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

+
var thread = threads.start(function(){
+    //do something
+});
+thread.waitFor();
+thread.setTimeout(function(){
+    //do something
+}, 1000);
+

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

+

参见timers.setTimeout()

+

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

+
log("当前线程(主线程):" + threads.currentThread());
+
+var thread = threads.start(function(){
+    //设置一个空的定时来保持线程的运行状态
+    setInterval(function(){}, 1000);
+});
+
+sleep(1000);
+thread.setTimeout(function(){
+    log("当前线程(子线程):" + threads.currentThread());
+    exit();
+}, 1000);
+

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

+

参见timers.setInterval()

+

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

+

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

+

参见timers.setImmediate()

+

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

+

Thread.clearInterval(id)#

+

参见timers.clearInterval()

+

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

+

Thread.clearTimeout(id)#

+

参见timers.clearTimeout()

+

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

+

Thread.clearImmediate(id)#

+

参见timers.clearImmediate()

+

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

+

线程安全#

+

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

+

引用维基百科的解释:

+
+

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

+
+

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

+

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

+

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

+

线程不安全的代码如下:

+
var i = 0;
+threads.start(function(){
+    while(true){
+        log(i++);
+    }
+});
+while(true){
+    log(i++);
+}
+

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

+

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

+
//atomic返回的对象保证了自增的原子性
+var i = threads.atomic();
+threads.start(function(){
+    while(true){
+        log(i.getAndIncrement());
+    }
+});
+while(true){
+    log(i.getAndIncrement());
+}
+

或者:

+
//锁保证了操作的原子性
+var lock = threads.lock();
+var i = 0;
+threads.start(function(){
+    while(true){
+        lock.lock();
+        log(i++);
+        lock.unlock();
+    }
+});
+while(true){
+    lock.lock();
+    log(i++);
+    lock.unlock();
+}
+

或者:

+
//sync函数会把里面的函数加上同步锁,使得在同一时刻最多只能有一个线程执行这个函数
+var i = 0;
+var getAndIncrement = sync(function(){
+    return i++;
+});
+threads.start(function(){
+    while(true){
+        log(getAndIncrement());
+    }
+});
+while(true){
+    log(getAndIncrement());
+}
+

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

+
var nums = new java.util.Vector();
+nums.add(123);
+nums.add(456);
+toast("长度为" + nums.size());
+toast("第一个元素为" + nums.get(0));
+

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

+
var nums = [];
+var numsLock = threads.lock();
+threads.start(function(){
+    //向数组添加元素123
+    numsLock.lock();
+    nums.push(123);
+    log("线程: %s, 数组: %s", threads.currentThread(), nums);
+    numsLock.unlock();
+});
+
+threads.start(function(){
+    //向数组添加元素456
+    numsLock.lock();
+    nums.push(456);
+    log("线程: %s, 数组: %s", threads.currentThread(), nums);
+    numsLock.unlock();
+});
+
+//删除数组最后一个元素
+numsLock.lock();
+nums.pop();
+log("线程: %s, 数组: %s", threads.currentThread(), nums);
+numsLock.unlock();
+

sync(func)#

+
+

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

+
var i = 0;
+function add(x){
+    i += x;
+}
+
+var syncAdd = sync(add);
+syncAdd(10);
+toast(i);
+

线程通信#

+

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

+

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

+
var sum = threads.disposable();
+//启动子线程计算
+threads.start(function(){
+    var s = 0;
+    //从1加到10000
+    for(var i = 1; i <= 10000; i++){
+        s += i;
+    }
+    //通知主线程接收结果
+    sum.setAndNotify(s);
+});
+//blockedGet()用于等待结果
+toast("sum = " + sum.blockedGet());
+

如果上述代码用Condition实现:

+
//新建一个锁
+var lock = threads.lock();
+//新建一个条件,即"计算完成"
+var complete = lock.newCondition();
+var sum = 0;
+threads.start(function(){
+    //从1加到10000
+    for(var i = 1; i <= 10000; i++){
+        sum += i;
+    }
+    //通知主线程接收结果
+    lock.lock();
+    complete.signal();
+    lock.unlock();
+});
+//等待计算完成
+lock.lock();
+complete.await();
+lock.unlock();
+//打印结果
+toast("sum = " + sum);
+

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

+
//新建一个emitter, 并指定回调执行的线程为当前线程
+var sum = events.emitter(threads.currentThread());
+threads.start(function(){
+    var s = 0;
+    //从1加到10000
+    for(var i = 1; i <= 10000; i++){
+        s += i;
+    }
+    //发送事件result通知主线程接收结果
+    sum.emit('result', s);
+});
+sum.on('result', function(s){
+    toastLog("sum = " + s + ", 当前线程: " + threads.currentThread());
+});
+

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

Timers#

Stability: 2 - Stable

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

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

-

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

-
-

预定立即执行的 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])#

预定在 delay 毫秒之后执行的单次 callback。 返回一个用于 clearTimeout() 的 id。

callback 可能不会精确地在 delay 毫秒被调用。 Auto.js 不能保证回调被触发的确切时间,也不能保证它们的顺序。 回调会在尽可能接近所指定的时间上调用。

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

+

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

+
+

预定立即执行的 callback,它是在 I/O 事件的回调之后被触发。 返回一个用于 clearImmediate() 的 id。

+

当多次调用 setImmediate() 时,callback 函数会按照它们被创建的顺序依次执行。 每次事件循环迭代都会处理整个回调队列。 如果一个立即定时器是被一个正在执行的回调排入队列的,则该定时器直到下一次事件循环迭代才会被触发。

setImmediate()、setInterval() 和 setTimeout() 方法每次都会返回表示预定的计时器的id。 它们可用于取消定时器并防止触发。

+

clearInterval(id)#

+
+

取消一个由 setInterval() 创建的循环定时任务。

+

例如:

+
//每5秒就发出一次hello
+var id = setInterval(function(){
+    toast("hello");
+}, 5000);
+//1分钟后取消循环
+setTimeout(function(){
+    clearInterval(id);
+}, 60 * 1000);
+

clearTimeout(id)#

+
+

取消一个由 setTimeout() 创建的定时任务。

clearImmediate(id)#

取消一个由 setImmediate() 创建的 Immediate 对象。

-

clearInterval(id)#

-
-

取消一个由 setInterval() 创建的 Timeout 对象。

-

clearTimeout(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 @@

目录

@@ -89,77 +97,146 @@

App#

-

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

-

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

+

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

+

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

+

app.launchApp(appName)#

+
+

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

+

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

+
launchApp("Auto.js");
+

app.launch(packageName)#

+
+

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

+

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

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

app.launchPackage(packageName)#

+
+

相当于app.launch(packageName)

+

app.getPackageName(appName)#

+
+

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

+

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

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

app.getAppName(packageName)#

+
+

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

+

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

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

app.openAppSetting(packageName)#

+
+

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

+

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

app.viewFile(path)#

-

用其他应用查看文件。

-

app.editFile(path)#

+

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

+

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

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

app.editFile(path)#

-

用其他应用编辑文件。

-

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。

-

app.takePhoto(path)#

-
-

调用相机应用拍照,完成后保存到路径path。

+

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

app.sendEmail(options)#

根据选项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 可以通过多种方式促进组件之间的通信,但其基本用例主要包括以下三个:

+ +

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

+

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

+

+

app.intent(options)#

返回用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])#

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

-

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

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

console.input(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以上点按与手势模拟#

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

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

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

    +

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

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

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

    +

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

    +
    Stability: 2 - Stable

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

    click(x, y)#

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

    • x1 <number> 滑动的起始坐标的x值
    • y1 <number> 滑动的起始坐标的y值
    • @@ -152,22 +178,17 @@

      例如手指捏合:

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

      setScreenMetrics(width, height)#

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

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

      -

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

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

      那么在其他设备上AutoJs会自动放缩坐标以便脚本仍然有效。

      -

      RootAutomator#

      +

    RootAutomator#

    Stability: 2 - Stable

    RootAutomator是一个使用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, 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权限,可以实现任意位置的点击、滑动等。

    Engines#

    -
    Stability: 2 - Stable

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

    -

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

    +
    Stability: 2 - Stable

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

    +

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

    +
    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。返回一个ScriptExecution对象。

    -

    engines.execAutoFile(path[, config])#

    +

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

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

    engines.execAutoFile(path[, 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包括:

    - -

    停止脚本引擎的执行。

    +

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

    ScriptConfig#

    脚本执行时的配置。

    delay#

    -

    延迟执行的毫秒数

    +
    +

    延迟执行的毫秒数

    interval#

    -

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

    +
    +

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

    loopTimes#

    -

    循环运行次数

    +
    +

    循环运行次数

    getPath()#

    -

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

    -

    ScriptSource#

    -

    脚本执行时的源码对象。可以是字符串源码、文件源码等。

    -

    如果该源码是文件脚本,则可以通过toString()得到该文件的路径。

    -

    getName()#

    -

    返回该源码的名称。

    -

    getEngineName()#

    -

    返回执行该源码的脚本引擎的名称。

    +
    +

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

    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 - Stable

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

    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对应的按键被按下会调用该函数。可用的按键名称参见[Keys][]。

    +

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

    例如:

    //启用按键监听
     events.observeKey();
    @@ -198,7 +219,7 @@ events.onKeyDown("home", function(event){
     

    events.observeTouch()#

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

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

    -

    没有root权限调用该函数则什么也不会发生。(注意: 这个行为未来可能会更改为抛出异常)

    +

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

    events.setTouchEventTimeout(timeout)#

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

    -

    事件: 'key_down'#

    +

    例如:

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

    事件: 'key_down'#

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

    +

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

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

    事件: 'notification'#

    -

    当有应用发出通知时会触发该事件。

    +

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

    例如:

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

    注意: 这是一个实验性功能。实测只有某些情况下的通知才能被正确捕捉。

    +

    Notification#

    +

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

    +

    Notification.number#

    +
    +

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

    +

    Notification.when#

    +
    +

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

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

    Notification.getPackageName()#

    +
    +

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

    +

    Notification.getTitle()#

    +
    +

    获取通知的标题。

    +

    Notification.getText()#

    +
    +

    获取通知的内容。

    +

    Notification.click()#

    +

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

    +

    Notification.delete()#

    +

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

    +

    KeyEvent#

    +
    Stability: 2 - Stable

    KeyEvent.getAction()#

    +

    返回事件的动作。包括:

    + +

    KeyEvent.getKeyCode()#

    +

    返回按键的键值。包括:

    + +

    KeyEvent.getEventTime()#

    +
    +

    返回事件发生的时间戳。

    +

    KeyEvent.getDownTime()#

    +

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

    +

    KeyEvent.keyCodeToString(keyCode)#

    +

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

    +

    keys#

    +
    Stability: 2 - Stable

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

    +

    EventEmitter#

    Stability: 2 - Stable

    EventEmitter.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 - Stable

    KeyEvent.getAction()#

    -

    返回事件的动作。包括:

    - -

    KeyEvent.getKeyCode()#

    -

    返回按键的键值。包括:

    - -

    KeyEvent.getEventTime()#

    -

    返回事件发生的时间戳。返回值的类型是number。

    -

    KeyEvent.getDownTime()#

    -

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

    -

    KeyEvent.keyCodeToString(keyCode)#

    -

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

    -

    Keys#

    -
    Stability: 2 - Stable

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

    - 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 - Stable

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

    +

    一次性的文件读写可以直接使用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所在的文件夹存在。

    +

    确保路径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的所有内容并返回一个字节数组。

    -

    注意,该数组是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'])#

    复制文件,返回是否复制成功。例如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卡,即外部存储器。

    +
    +

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

    +

    files.cwd()#

    +
    +

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

    +

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

    files.listDir(path[, filter])#

    列出文件夹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> 文件读写的缓冲区大小。

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

    设置该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)#

    Threads#

    -
    Stability: 1 - Experiment

    threads模块提供了多线程支持。可以启动新线程来运行脚本。新线程会在脚本停止时也自动停止。

    -

    但是,在新线程中暂时不能使用timers模块的函数,包括setTimeout, setInterval等。而且目前在新线程调用exit()函数时只会退出当前线程。

    +
    Stability: 1 - Experiment

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

    +

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

    +

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

    +

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

    threads.start(action)#

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

    +
    +

    返回当前线程。

    +

    threads.disposable()#

    +
    +

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

    +

    threads.atomic([initialValue])#

    +
    +

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

    +

    threads.lock()#

    +
    +

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

    +

    Thread#

    +

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

    +

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

    +
    var thread = threads.start(function(){
    +    //在子线程执行的定时器
    +    setInterval(function(){
    +        log("子线程:" + threads.currentThread());
    +    }, 1000);
    +});
    +
    +log("当前线程为主线程:" + threads.currentThread());
    +
    +//等待子线程启动
    +thread.waitFor();
    +//在子线程执行的定时器
    +thread.setTimeout(function(){
    +    //这段代码会在子线程执行
    +    log("当前线程为子线程:" + threads.currentThread());
    +}, 2000);
    +
    +sleep(30 * 1000);
    +thread.interrupt();
    +

    Thread.interrupt()#

    +

    中断线程运行。

    +

    Thread.join([timeout])#

    +
    +

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

    +

    例如:

    +
    var sum = 0;
    +//启动子线程计算1加到10000
    +var thread = threads.start(function(){
    +    for(var i = 0; i < 10000; i++){
    +        sum += i;
    +    }
    +});
    +//等待该线程完成
    +thread.join();
    +toast("sum = " + sum);
    +

    isAlive()#

    +
    +

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

    +

    waitFor()#

    +

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

    +
    var thread = threads.start(function(){
    +    //do something
    +});
    +thread.waitFor();
    +thread.setTimeout(function(){
    +    //do something
    +}, 1000);
    +

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

    +

    参见timers.setTimeout()

    +

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

    +
    log("当前线程(主线程):" + threads.currentThread());
    +
    +var thread = threads.start(function(){
    +    //设置一个空的定时来保持线程的运行状态
    +    setInterval(function(){}, 1000);
    +});
    +
    +sleep(1000);
    +thread.setTimeout(function(){
    +    log("当前线程(子线程):" + threads.currentThread());
    +    exit();
    +}, 1000);
    +

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

    +

    参见timers.setInterval()

    +

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

    +

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

    +

    参见timers.setImmediate()

    +

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

    +

    Thread.clearInterval(id)#

    +

    参见timers.clearInterval()

    +

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

    +

    Thread.clearTimeout(id)#

    +

    参见timers.clearTimeout()

    +

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

    +

    Thread.clearImmediate(id)#

    +

    参见timers.clearImmediate()

    +

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

    +

    线程安全#

    +

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

    +

    引用维基百科的解释:

    +
    +

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

    +
    +

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

    +

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

    +

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

    +

    线程不安全的代码如下:

    +
    var i = 0;
    +threads.start(function(){
    +    while(true){
    +        log(i++);
    +    }
    +});
    +while(true){
    +    log(i++);
    +}
    +

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

    +

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

    +
    //atomic返回的对象保证了自增的原子性
    +var i = threads.atomic();
    +threads.start(function(){
    +    while(true){
    +        log(i.getAndIncrement());
    +    }
    +});
    +while(true){
    +    log(i.getAndIncrement());
    +}
    +

    或者:

    +
    //锁保证了操作的原子性
    +var lock = threads.lock();
    +var i = 0;
    +threads.start(function(){
    +    while(true){
    +        lock.lock();
    +        log(i++);
    +        lock.unlock();
    +    }
    +});
    +while(true){
    +    lock.lock();
    +    log(i++);
    +    lock.unlock();
    +}
    +

    或者:

    +
    //sync函数会把里面的函数加上同步锁,使得在同一时刻最多只能有一个线程执行这个函数
    +var i = 0;
    +var getAndIncrement = sync(function(){
    +    return i++;
    +});
    +threads.start(function(){
    +    while(true){
    +        log(getAndIncrement());
    +    }
    +});
    +while(true){
    +    log(getAndIncrement());
    +}
    +

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

    +
    var nums = new java.util.Vector();
    +nums.add(123);
    +nums.add(456);
    +toast("长度为" + nums.size());
    +toast("第一个元素为" + nums.get(0));
    +

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

    +
    var nums = [];
    +var numsLock = threads.lock();
    +threads.start(function(){
    +    //向数组添加元素123
    +    numsLock.lock();
    +    nums.push(123);
    +    log("线程: %s, 数组: %s", threads.currentThread(), nums);
    +    numsLock.unlock();
    +});
    +
    +threads.start(function(){
    +    //向数组添加元素456
    +    numsLock.lock();
    +    nums.push(456);
    +    log("线程: %s, 数组: %s", threads.currentThread(), nums);
    +    numsLock.unlock();
    +});
    +
    +//删除数组最后一个元素
    +numsLock.lock();
    +nums.pop();
    +log("线程: %s, 数组: %s", threads.currentThread(), nums);
    +numsLock.unlock();
    +

    sync(func)#

    +
    +

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

    +
    var i = 0;
    +function add(x){
    +    i += x;
    +}
    +
    +var syncAdd = sync(add);
    +syncAdd(10);
    +toast(i);
    +

    线程通信#

    +

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

    +

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

    +
    var sum = threads.disposable();
    +//启动子线程计算
    +threads.start(function(){
    +    var s = 0;
    +    //从1加到10000
    +    for(var i = 1; i <= 10000; i++){
    +        s += i;
    +    }
    +    //通知主线程接收结果
    +    sum.setAndNotify(s);
    +});
    +//blockedGet()用于等待结果
    +toast("sum = " + sum.blockedGet());
    +

    如果上述代码用Condition实现:

    +
    //新建一个锁
    +var lock = threads.lock();
    +//新建一个条件,即"计算完成"
    +var complete = lock.newCondition();
    +var sum = 0;
    +threads.start(function(){
    +    //从1加到10000
    +    for(var i = 1; i <= 10000; i++){
    +        sum += i;
    +    }
    +    //通知主线程接收结果
    +    lock.lock();
    +    complete.signal();
    +    lock.unlock();
    +});
    +//等待计算完成
    +lock.lock();
    +complete.await();
    +lock.unlock();
    +//打印结果
    +toast("sum = " + sum);
    +

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

    +
    //新建一个emitter, 并指定回调执行的线程为当前线程
    +var sum = events.emitter(threads.currentThread());
    +threads.start(function(){
    +    var s = 0;
    +    //从1加到10000
    +    for(var i = 1; i <= 10000; i++){
    +        s += i;
    +    }
    +    //发送事件result通知主线程接收结果
    +    sum.emit('result', s);
    +});
    +sum.on('result', function(s){
    +    toastLog("sum = " + s + ", 当前线程: " + threads.currentThread());
    +});
    +

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

    +
    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 - Stable

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

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

    -

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

    -
    -

    预定立即执行的 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])#

    预定在 delay 毫秒之后执行的单次 callback。 返回一个用于 clearTimeout() 的 id。

    callback 可能不会精确地在 delay 毫秒被调用。 Auto.js 不能保证回调被触发的确切时间,也不能保证它们的顺序。 回调会在尽可能接近所指定的时间上调用。

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

    +

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

    +
    +

    预定立即执行的 callback,它是在 I/O 事件的回调之后被触发。 返回一个用于 clearImmediate() 的 id。

    +

    当多次调用 setImmediate() 时,callback 函数会按照它们被创建的顺序依次执行。 每次事件循环迭代都会处理整个回调队列。 如果一个立即定时器是被一个正在执行的回调排入队列的,则该定时器直到下一次事件循环迭代才会被触发。

    setImmediate()、setInterval() 和 setTimeout() 方法每次都会返回表示预定的计时器的id。 它们可用于取消定时器并防止触发。

    +

    clearInterval(id)#

    +
    +

    取消一个由 setInterval() 创建的循环定时任务。

    +

    例如:

    +
    //每5秒就发出一次hello
    +var id = setInterval(function(){
    +    toast("hello");
    +}, 5000);
    +//1分钟后取消循环
    +setTimeout(function(){
    +    clearInterval(id);
    +}, 60 * 1000);
    +

    clearTimeout(id)#

    +
    +

    取消一个由 setTimeout() 创建的定时任务。

    clearImmediate(id)#

    取消一个由 setImmediate() 创建的 Immediate 对象。

    -

    clearInterval(id)#

    -
    -

    取消一个由 setInterval() 创建的 Timeout 对象。

    -

    clearTimeout(id)#

    -
    -

    取消一个由 setTimeout() 创建的 Timeout 对象。