超全的auto.js基础操作,目前是autoX.js的控制方式。2023年9月23日更新!(第4/4章)

02_auto.js基础操作4/4

应用

打开应用

var appName = rawInput("请输入应用名称");
launchApp(appName);

发送意图-文本消息分享

var content = rawInput('请输入要分享的文本');
app.startActivity({
    action: "android.intent.action.SEND",
    type: "text/*",
    extras: {
      "android.intent.extra.TEXT": content
    },
    packageName: "com.tencent.mobileqq",
    className: "com.tencent.mobileqq.activity.JumpActivity"
});

强制停止应用

"auto";

var appName = rawInput("请输入应用名称");
openAppSetting(getPackageName(appName));
while(!click("强制停止"));

卸载应用

//输入应用名称
var appName = rawInput('请输入要卸载的应用名称');
//获取应用包名
var packageName = getPackageName(appName);
if(!packageName){
    toast("应用不存在!");
}else{
    //卸载应用
    app.uninstall(packageName);
}

应用工具

var i = dialogs.select("请选择工具", "获取应用包名", "打开应用详情页", "卸载应用");

if(i == -1){
    alert("没有选择任何工具!");
}

switch(i){
case 0:
    //获取应用包名
    appName = rawInput("请输入应用名称", "QQ");
    packageName = getPackageName(appName);
    toast(packageName);
    setClip(packageName);
    toast("已复制到剪贴板");
    break;
case 1:
    //打开应用详情页
    appName = rawInput("请输入应用名称", "微信");
    openAppSetting(getPackageName(appName));
    break;
case 2:
    //卸载应用
    appName = rawInput("请输入应用名称");
    packageName = getPackageName(appName);
    if(packageName == ""){
        toast("应用不存在");
    }else if(confirm("确定卸载应用" + packageName + "吗?")){
        app.uninstall(packageName);
    }
    break;
}

GoogleMLkit

OCR识别

let img = images.read("./1.png")
let start = new Date()
// 识别图片中的文字,返回完整识别信息(兼容百度OCR格式)。
//可选语言:拉丁 "la" , 中文 "zh" ,梵文 "sa" ,日语 "ja" , 韩语 "ko"
let result = gmlkit.ocr(img, "zh")
log('OCR识别耗时:' + (new Date() - start) + 'ms')
//排序 sort()会改变原对象,sorted() 不会改变原对象,而是返回新对象
result.sort()
//或者
//let newResult = result.sorted()
log("识别信息: " + result)
log("-------------------------------------------")
log("文本识别信息: " + result.text)
log("-------------------------------------------")
toastLog("json识别信息: " + JSON.stringify(result))
// 回收图片
img.recycle()

// project.json

{
  "abis": [
    "arm64-v8a",
    "armeabi-v7a",
    "x86",
    "x86_64"
  ],
  "assets": [
    {
      "form": "file:///android_asset/mlkit-google-ocr-models",
      "to": "/mlkit-google-ocr-models"
    }
  ],
  "buildDir": "build",
  "build": {
    "build_id": null,
    "build_number": 0,
    "build_time": 0
  },
  "useFeatures": [],
  "icon": null,
  "ignoredDirs": [],
  "launchConfig": {
    "displaySplash": false,
    "hideLauncher": false,
    "hideLogs": false,
    "stableMode": false,
    "volumeUpcontrol": false,
    "permissions": [],
    "serviceDesc": "使脚本自动操作(点击、长按、滑动等)所需,若关闭则只能执行不涉及自动操作的脚本。",
    "splashIcon": null,
    "splashText": "Powered by Google ML Kit ocr"
  },
  "libs": [
    "libmlkit_google_ocr_pipeline.so",
    "libjackpal-androidterm5.so",
    "libjackpal-termexec2.so"
  ],
  "main": "main.js",
  "name": "Google MlKitOCR",
  "outputPath": null,
  "packageName": "com.script.gmlkitocr",
  "scripts": {},
  "signingConfig": {
    "alias": null,
    "keystore": null
  },
  "sourcePath": null,
  "versionCode": 1,
  "versionName": "1.0.0"
}

OCR截图识别

let currentEngine = engines.myEngine()
let runningEngines = engines.all()
let currentSource = currentEngine.getSource() + ''
if (runningEngines.length > 1) {
  runningEngines.forEach(compareEngine => {
    let compareSource = compareEngine.getSource() + ''
    if (currentEngine.id !== compareEngine.id && compareSource === currentSource) {
      // 强制关闭同名的脚本
      compareEngine.forceStop()
    }
  })
}

if (!requestScreenCapture()) {
  toastLog('请求截图权限失败')
  exit()
}

sleep(1000)

// 识别结果和截图信息
let result = []
let img = null
let running = true
let capturing = true

/**
 * 截图并识别OCR文本信息
 */
function captureAndOcr() {
  capturing = true
  img && img.recycle()
  img = captureScreen()
  if (!img) {
    toastLog('截图失败')
  }
  let start = new Date()
  //结果转数组:层级:3
  result = gmlkit.ocr(img,"zh").toArray(3);
  log(result);
  toastLog('耗时' + (new Date() - start) + 'ms')
  capturing = false
}

captureAndOcr()

// 获取状态栏高度
let offset = -getStatusBarHeightCompat()
//let offset = 0;

// 绘制识别结果
let window = floaty.rawWindow(
  <canvas id="canvas" layout_weight="1" />
);

// 设置悬浮窗位置
ui.post(() => {
  window.setPosition(0, offset)
  window.setSize(device.width, device.height)
  window.setTouchable(false)
})

// 操作按钮
let clickButtonWindow = floaty.rawWindow(
  <vertical>
    <button id="captureAndOcr" text="截图识别" />
    <button id="closeBtn" text="退出" />
  </vertical>
);
ui.run(function () {
  clickButtonWindow.setPosition(device.width / 2 - ~~(clickButtonWindow.getWidth() / 2), device.height * 0.65)
})

// 点击识别
clickButtonWindow.captureAndOcr.click(function () {
  result = []
  ui.run(function () {
    clickButtonWindow.setPosition(device.width, device.height)
  })
  setTimeout(() => {
    threads.start(()=>{
      captureAndOcr()
      ui.run(function () {
        clickButtonWindow.setPosition(device.width / 2 - ~~(clickButtonWindow.getWidth() / 2), device.height * 0.65)
      })
    })
  }, 500)
})

// 点击关闭
clickButtonWindow.closeBtn.click(function () {
  exit()
})

let Typeface = android.graphics.Typeface
let paint = new Paint()
paint.setStrokeWidth(1)
paint.setTypeface(Typeface.DEFAULT_BOLD)
paint.setTextAlign(Paint.Align.LEFT)
paint.setAntiAlias(true)
paint.setStrokeJoin(Paint.Join.ROUND)
paint.setDither(true)
window.canvas.on('draw', function (canvas) {
  if (!running || capturing) {
    return
  }
  // 清空内容
  canvas.drawColor(0xFFFFFF, android.graphics.PorterDuff.Mode.CLEAR)
  if (result && result.length > 0) {
    for (let i = 0; i < result.length; i++) {
      let ocrResult = result[i]
      drawRectAndText(ocrResult.text + ' #信心:' + ocrResult.confidence.toFixed(2), ocrResult.bounds, '#00ff00', canvas, paint);
    }
  }
})

setInterval(() => { }, 10000)
events.on('exit', () => {
  // 标记停止 避免canvas导致闪退
  running = false
  // 回收图片
  img && img.recycle()
  // 撤销监听
  window.canvas.removeAllListeners()
  
})

/**
 * 绘制文本和方框
 *
 * @param {*} desc
 * @param {*} rect
 * @param {*} colorStr
 * @param {*} canvas
 * @param {*} paint
 */
function drawRectAndText (desc, rect, colorStr, canvas, paint) {
  let color = colors.parseColor(colorStr)

  paint.setStrokeWidth(1)
  paint.setStyle(Paint.Style.STROKE)
  // 反色
  paint.setARGB(255, 255 - (color >> 16 & 0xff), 255 - (color >> 8 & 0xff), 255 - (color & 0xff))
  canvas.drawRect(rect, paint)
  paint.setARGB(255, color >> 16 & 0xff, color >> 8 & 0xff, color & 0xff)
  paint.setStrokeWidth(1)
  paint.setTextSize(20)
  paint.setStyle(Paint.Style.FILL)
  canvas.drawText(desc, rect.left, rect.top, paint)
  paint.setTextSize(10)
  paint.setStrokeWidth(1)
  paint.setARGB(255, 0, 0, 0)
}

/**
 * 获取状态栏高度
 *
 * @returns
 */
function getStatusBarHeightCompat () {
  let result = 0
  let resId = context.getResources().getIdentifier("status_bar_height", "dimen", "android")
  if (resId > 0) {
    result = context.getResources().getDimensionPixelOffset(resId)
  }
  if (result <= 0) {
    result = context.getResources().getDimensionPixelOffset(R.dimen.dimen_25dp)
  }
  return result
}

OCR识别点击

//requestScreenCapture()
var mainActivity = "org.autojs.autojs.ui.main.MainActivity"
if (currentActivity != mainActivity) {
    app.startActivity({
        packageName: "org.autojs.autoxjs.v6",
        className: mainActivity,
    });
    waitForActivity(mainActivity)
}
requestScreenCapture()
sleep(1000)
let img = captureScreen()
let start = new Date()
let result = gmlkit.ocr(img, "zh")
toastLog('OCR识别耗时:' + (new Date() - start) + 'ms')
let managerBtn = result.find(3, e => e.text == "管理")
if (managerBtn) click(managerBtn.bounds)
sleep(500)
let homeBtn = result.find(3, e => e.text == "主页")
if (homeBtn) click(homeBtn.bounds)
sleep(500)
let docBtn = result.find(3, e => e.text == "文档")
if (docBtn) press(docBtn.bounds, 500)
// 回收图片
img.recycle()

HTTP网络请求

获取网页

var url = "www.baidu.com";
var res = http.get(url);
if(res.statusCode == 200){
    toast("请求成功");
    console.show();
    log(res.body.string());
}else{
    toast("请求失败:" + res.statusMessage);
}

文件上传

//如果遇到SocketTimeout的异常,重新多运行几次脚本即可

console.show();
example1();
example2();
example3();
example4();
example5();

function example1(){
    var res = http.postMultipart("http://posttestserver.com/post.php", {
        "file": open("/sdcard/1.txt")
    });
    log("例子1:");
    log(res.body.string());
}

function example2(){
    var res = http.postMultipart("http://posttestserver.com/post.php", {
        "file": ["1.txt", "/sdcard/1.txt"]
    });
    log("例子2:");
    log(res.body.string());
}

function example3(){
    var res = http.postMultipart("http://posttestserver.com/post.php", {
        "file": ["1.txt", "text/plain", "/sdcard/1.txt"]
    });
    log("例子3:");
    log(res.body.string());
}

function example4(){
    var res = http.postMultipart("http://posttestserver.com/post.php", {
        "file": open("/sdcard/1.txt"),
        "aKey": "aValue"
    });
    log("例子4:");
    log(res.body.string());
}

文件下载

var url = "http://www.autojs.org/assets/uploads/profile/3-profileavatar.png";
var res = http.get(url);
if(res.statusCode != 200){
    toast("请求失败");
}
files.writeBytes("/sdcard/1.png", res.body.bytes());
toast("下载成功");
app.viewFile("/sdcard/1.png");

javascript

数字

a = 5;
b = 6;
c = -1;
x = 1.5;
y = a * x * x + b * x * c;
log("y = " + y);
openConsole();

E4X

/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

print("----------------------------------------");

// Use the XML constructor to parse an string into an XML object
var John = "<employee><name>John</name><age>25</age></employee>";
var Sue ="<employee><name>Sue</name><age>32</age></employee>";
var tagName = "employees";
var employees = new XML("<" + tagName +">" + John + Sue + "</" + tagName +">");
print("The employees XML object constructed from a string is:\n" + employees);

print("----------------------------------------");

// Use an XML literal to create an XML object
var order = <order>
   <customer>
      <firstname>John</firstname>
      <lastname>Doe</lastname>
   </customer>
   <item>
      <description>Big Screen Television</description>
      <price>1299.99</price>
      <quantity>1</quantity>
   </item>
</order>

// Construct the full customer name
var name = order.customer.firstname + " " + order.customer.lastname;

// Calculate the total price
var total = order.item.price * order.item.quantity;

print("The order XML object constructed using a literal is:\n" + order);
print("The total price of " + name + "'s order is " + total);

print("----------------------------------------");

// construct a new XML object using expando and super-expando properties
var order = <order/>;
order.customer.name = "Fred Jones";
order.customer.address.street = "123 Long Lang";
order.customer.address.city = "Underwood";
order.customer.address.state = "CA";
order.item[0] = "";
order.item[0].description = "Small Rodents";
order.item[0].quantity = 10;
order.item[0].price = 6.95;

print("The order custructed using expandos and super-expandos is:\n" + order);

// append a new item to the order
order.item += <item><description>Catapult</description><price>139.95</price></item>;

print("----------------------------------------");

print("The order after appending a new item is:\n" + order);

print("----------------------------------------");

// dynamically construct an XML element using embedded expressions
var tagname = "name";
var attributename = "id";
var attributevalue = 5;
var content = "Fred";

var x = <{tagname} {attributename}={attributevalue}>{content}</{tagname}>;

print("The dynamically computed element value is:\n" + x.toXMLString());

print("----------------------------------------");

// Create a SOAP message
var message = <soap:Envelope
      xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
      soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
   <soap:Body>
      <m:GetLastTradePrice xmlns:m="http://mycompany.com/stocks">
         <symbol>DIS</symbol>
      </m:GetLastTradePrice>
   </soap:Body>
</soap:Envelope>

// declare the SOAP and stocks namespaces
var soap = new Namespace("http://schemas.xmlsoap.org/soap/envelope/");
var stock = new Namespace ("http://mycompany.com/stocks");

// extract the soap encoding style and body from the soap message
var encodingStyle = message.@soap::encodingStyle;

print("The encoding style of the soap message is specified by:\n" + encodingStyle);

// change the stock symbol
message.soap::Body.stock::GetLastTradePrice.symbol = "MYCO";

var body = message.soap::Body;

print("The body of the soap message is:\n" + body);

print("----------------------------------------");

// create an manipulate an XML object using the default xml namespace

default xml namespace = "http://default.namespace.com";
var x = <x/>;
x.a = "one";
x.b = "two";
x.c = <c xmlns="http://some.other.namespace.com">three</c>;

print("XML object constructed using the default xml namespace:\n" + x);

default xml namespace="";

print("----------------------------------------");

var order = <order id = "123456" timestamp="Mon Mar 10 2003 16:03:25 GMT-0800 (PST)">
   <customer>
      <firstname>John</firstname>
      <lastname>Doe</lastname>
   </customer>
   <item id="3456">
      <description>Big Screen Television</description>
      <price>1299.99</price>
      <quantity>1</quantity>
   </item>
   <item id = "56789">
      <description>DVD Player</description>
      <price>399.99</price>
      <quantity>1</quantity>
   </item>
</order>;


// get the customer element from the orderprint("The customer is:\n" + order.customer);

// get the id attribute from the order
print("The order id is:" + order.@id);

// get all the child elements from the order element
print("The children of the order are:\n" + order.*); 

// get the list of all item descriptions
print("The order descriptions are:\n" + order.item.description); 


// get second item by numeric index
print("The second item is:\n" + order.item[1]);

// get the list of all child elements in all item elements
print("The children of the items are:\n" + order.item.*);

// get the second child element from the order by index
print("The second child of the order is:\n" + order.*[1]);

// calculate the total price of the order
var totalprice = 0;
for each (i in order.item) {
 totalprice += i.price * i.quantity;
}
print("The total price of the order is: " + totalprice);

print("----------------------------------------");

var e = <employees>
   <employee id="1"><name>Joe</name><age>20</age></employee>
   <employee id="2"><name>Sue</name><age>30</age></employee>
</employees>;

// get all the names in e
print("All the employee names are:\n" + e..name);

// employees with name Joe
print("The employee named Joe is:\n" + e.employee.(name == "Joe"));

// employees with id's 1 & 2
print("Employees with ids 1 & 2:\n" + e.employee.(@id == 1 || @id == 2)); 

// name of employee with id 1
print("Name of the the employee with ID=1: " + e.employee.(@id == 1).name);

print("----------------------------------------");

openConsole();

HelloWorld

log("Hello world!!!");
toast("Hello, AutoJs!");
console.show();

PaddleOCR

PaddleOCR文本识别

let img = images.read("./0.jpg")
// PaddleOCR 移动端提供了两种模型:ocr_v2_for_cpu与ocr_v2_for_cpu(slim),此选项用于选择加载的模型,默认true使用v2的slim版(速度更快),false使用v2的普通版(准确率更高)
let useSlim = true
let start = new Date()
// 识别图片中的文字,返回完整识别信息(兼容百度OCR格式)。
let result = paddle.ocr(img, useSlim)
log('OCR识别耗时:' + (new Date() - start) + 'ms')
// 可以使用简化的调用命令,默认参数:cpuThreadNum = 4, useSlim = true
// const result = paddle.ocr(img)
toastLog("完整识别信息: " + JSON.stringify(result))
start = new Date()
// 识别图片中的文字,只返回文本识别信息(字符串列表)。当前版本可能存在文字顺序错乱的问题 建议先使用detect后自行排序
const stringList = paddle.ocrText(img, useSlim)
log('OCR纯文本识别耗时:' + (new Date() - start) + 'ms')
// 可以使用简化的调用命令,默认参数:cpuThreadNum = 4, useSlim = true
// const stringList = paddle.ocrText(img)
toastLog("文本识别信息: " + JSON.stringify(stringList))

// 回收图片
img.recycle()
// 释放native内存,非必要,供万一出现内存泄露时使用
// paddle.release()


// project.json
{
  "abis": [
    "arm64-v8a",
    "armeabi-v7a",
    "x86",
    "x86_64"
  ],
  "assets": [
    {
      "form": "file:///android_asset/models",
      "to": "/models"
    }
  ],
  "buildDir": "build",
  "build": {
    "build_id": null,
    "build_number": 0,
    "build_time": 0
  },
  "useFeatures": [],
  "icon": null,
  "ignoredDirs": [],
  "launchConfig": {
    "displaySplash": false,
    "hideLauncher": false,
    "hideLogs": false,
    "stableMode": false,
    "volumeUpcontrol": false,
    "permissions": [],
    "serviceDesc": "使脚本自动操作(点击、长按、滑动等)所需,若关闭则只能执行不涉及自动操作的脚本。",
    "splashIcon": null,
    "splashText": "Powered by paddle ocr"
  },
  "libs": [
    "libc++_shared.so",
    "libpaddle_light_api_shared.so",
    "libhiai.so",
    "libhiai_ir.so",
    "libhiai_ir_build.so",
    "libNative.so",
    "libjackpal-androidterm5.so",
    "libjackpal-termexec2.so"
  ],
  "main": "PaddleOCR.js",
  "name": "PaddleOCR",
  "outputPath": null,
  "packageName": "com.script.paddleocr",
  "scripts": {},
  "signingConfig": {
    "alias": null,
    "keystore": null
  },
  "sourcePath": null,
  "versionCode": 1,
  "versionName": "1.0.0"
}

PaddleOCR文本识别-自定义模型路径

let img = images.read("./0.jpg")
// 新增:自定义模型路径(必须是绝对路径), files.path() 将相对路径转为绝对路径
let myModelPath = files.path("./models");
let start = new Date()
// 识别图片中的文字,返回完整识别信息(兼容百度OCR格式)。
let result = paddle.ocr(img, myModelPath)
log('OCR识别耗时:' + (new Date() - start) + 'ms')
toastLog("完整识别信息: " + JSON.stringify(result))
start = new Date()
// 识别图片中的文字,只返回文本识别信息(字符串列表)。当前版本可能存在文字顺序错乱的问题 建议先使用detect后自行排序
const stringList = paddle.ocrText(img, myModelPath)
log('OCR纯文本识别耗时:' + (new Date() - start) + 'ms')
toastLog("文本识别信息: " + JSON.stringify(stringList))

// 回收图片
img.recycle()
// 释放native内存,非必要,供万一出现内存泄露时使用
// paddle.release()


// project.json
{
  "abis": [
    "arm64-v8a",
    "armeabi-v7a",
    "x86",
    "x86_64"
  ],
  "assets": [
    {
      "form": "models",
      "to": "/models"
    }
  ],
  "buildDir": "build",
  "build": {
    "build_id": null,
    "build_number": 0,
    "build_time": 0
  },
  "useFeatures": [],
  "icon": null,
  "ignoredDirs": [],
  "launchConfig": {
    "displaySplash": false,
    "hideLauncher": false,
    "hideLogs": false,
    "stableMode": false,
    "volumeUpcontrol": false,
    "permissions": [],
    "serviceDesc": "使脚本自动操作(点击、长按、滑动等)所需,若关闭则只能执行不涉及自动操作的脚本。",
    "splashIcon": null,
    "splashText": "Powered by paddle ocr"
  },
  "libs": [
    "libc++_shared.so",
    "libpaddle_light_api_shared.so",
    "libhiai.so",
    "libhiai_ir.so",
    "libhiai_ir_build.so",
    "libNative.so",
    "libjackpal-androidterm5.so",
    "libjackpal-termexec2.so"
  ],
  "main": "PaddleOCR(自定义模型路径).js",
  "name": "PaddleOCR(自定义模型路径)",
  "outputPath": null,
  "packageName": "com.script.paddleocr.custommodel",
  "scripts": {},
  "signingConfig": {
    "alias": null,
    "keystore": null
  },
  "sourcePath": null,
  "versionCode": 1,
  "versionName": "1.0.0"
}

PaddleOCR-截图识别

let currentEngine = engines.myEngine()
let runningEngines = engines.all()
let currentSource = currentEngine.getSource() + ''
if (runningEngines.length > 1) {
  runningEngines.forEach(compareEngine => {
    let compareSource = compareEngine.getSource() + ''
    if (currentEngine.id !== compareEngine.id && compareSource === currentSource) {
      // 强制关闭同名的脚本
      compareEngine.forceStop()
    }
  })
}

if (!requestScreenCapture()) {
  toastLog('请求截图权限失败')
  exit()
}

sleep(1000)

// 识别结果和截图信息
let result = []
let img = null
let running = true
let capturing = true

/**
 * 截图并识别OCR文本信息
 */
function captureAndOcr() {
  capturing = true
  img && img.recycle()
  img = captureScreen()
  if (!img) {
    toastLog('截图失败')
  }
  let start = new Date()
  result = paddle.ocr(img);
  log(result);
  toastLog('耗时' + (new Date() - start) + 'ms')
  capturing = false
}

captureAndOcr()

// 获取状态栏高度
let offset = -getStatusBarHeightCompat()
//let offset = 0;

// 绘制识别结果
let window = floaty.rawWindow(
  <canvas id="canvas" layout_weight="1" />
);

// 设置悬浮窗位置
ui.post(() => {
  window.setPosition(0, offset)
  window.setSize(device.width, device.height)
  window.setTouchable(false)
})

// 操作按钮
let clickButtonWindow = floaty.rawWindow(
  <vertical>
    <button id="captureAndOcr" text="截图识别" />
    <button id="closeBtn" text="退出" />
  </vertical>
);
ui.run(function () {
  clickButtonWindow.setPosition(device.width / 2 - ~~(clickButtonWindow.getWidth() / 2), device.height * 0.65)
})

// 点击识别
clickButtonWindow.captureAndOcr.click(function () {
  result = []
  ui.run(function () {
    clickButtonWindow.setPosition(device.width, device.height)
  })
  setTimeout(() => {
    threads.start(()=>{
      captureAndOcr()
      ui.run(function () {
        clickButtonWindow.setPosition(device.width / 2 - ~~(clickButtonWindow.getWidth() / 2), device.height * 0.65)
      })
    })
  }, 500)
})

// 点击关闭
clickButtonWindow.closeBtn.click(function () {
  exit()
})

let Typeface = android.graphics.Typeface
let paint = new Paint()
paint.setStrokeWidth(1)
paint.setTypeface(Typeface.DEFAULT_BOLD)
paint.setTextAlign(Paint.Align.LEFT)
paint.setAntiAlias(true)
paint.setStrokeJoin(Paint.Join.ROUND)
paint.setDither(true)
window.canvas.on('draw', function (canvas) {
  if (!running || capturing) {
    return
  }
  // 清空内容
  canvas.drawColor(0xFFFFFF, android.graphics.PorterDuff.Mode.CLEAR)
  if (result && result.length > 0) {
    for (let i = 0; i < result.length; i++) {
      let ocrResult = result[i]
      drawRectAndText(ocrResult.words + ' #信心:' + ocrResult.confidence.toFixed(2), ocrResult.bounds, '#00ff00', canvas, paint);
    }
  }
})

setInterval(() => { }, 10000)
events.on('exit', () => {
  // 标记停止 避免canvas导致闪退
  running = false
  // 回收图片
  img && img.recycle()
  // 撤销监听
  window.canvas.removeAllListeners()
  
})

/**
 * 绘制文本和方框
 *
 * @param {*} desc
 * @param {*} rect
 * @param {*} colorStr
 * @param {*} canvas
 * @param {*} paint
 */
function drawRectAndText (desc, rect, colorStr, canvas, paint) {
  let color = colors.parseColor(colorStr)

  paint.setStrokeWidth(1)
  paint.setStyle(Paint.Style.STROKE)
  // 反色
  paint.setARGB(255, 255 - (color >> 16 & 0xff), 255 - (color >> 8 & 0xff), 255 - (color & 0xff))
  canvas.drawRect(rect, paint)
  paint.setARGB(255, color >> 16 & 0xff, color >> 8 & 0xff, color & 0xff)
  paint.setStrokeWidth(1)
  paint.setTextSize(20)
  paint.setStyle(Paint.Style.FILL)
  canvas.drawText(desc, rect.left, rect.top, paint)
  paint.setTextSize(10)
  paint.setStrokeWidth(1)
  paint.setARGB(255, 0, 0, 0)
}

/**
 * 获取状态栏高度
 *
 * @returns
 */
function getStatusBarHeightCompat () {
  let result = 0
  let resId = context.getResources().getIdentifier("status_bar_height", "dimen", "android")
  if (resId > 0) {
    result = context.getResources().getDimensionPixelOffset(resId)
  }
  if (result <= 0) {
    result = context.getResources().getDimensionPixelOffset(R.dimen.dimen_25dp)
  }
  return result
}

Shell命令

冻结网易云音乐

shell("pm disable com.netease.cloudmusic", true);

结束所有后台进程

shell("am kill-all", true);

解冻并打开网易云音乐

shell("pm enable com.netease.cloudmusic", true);
launchApp("网易云音乐");

锁屏

KeyCode("KEYCODE_POWER");
//或者 KeyCode(26);

TessractOCR

//此例子仅作为演示,无法运行,因为tessdata目录下没有训练数据,
//如需运行,可前往github下载完整例子:https://github.com/wilinz/autoxjs-tessocr
//导包
importClass(com.googlecode.tesseract.android.TessBaseAPI)
//新建OCR实例
var tessocr = new TessBaseAPI()
//请求截图权限
requestScreenCapture(false);
//3秒后开始
toastLog("3秒后截图")
sleep(3000)
toastLog("开始截图")
//截图 
var img = captureScreen();
//tessdata目录所在的文件夹,目录下放置训练数据
//训练数据下载地址:https://github.com/tesseract-ocr/tessdata/tree/4.0.0
var dataPath = files.path("./")
//初始化tessocr
//第二个参数是初始化的语言,是数据文件去掉扩展名后的文件名,多个语言用+连接
//训练数据文件夹必须命名为tessdata
//训练数据下载时是什么名字就是什么名字,不能改
var ok = tessocr.init(dataPath, "eng+chi_sim")
if (ok) {
    toastLog("初始化成功: " + tessocr.getInitLanguagesAsString())
} else {
    toastLog("初始化失败")
}
//设置图片
tessocr.setImage(img.getBitmap())
//打印文本结果
toastLog(tessocr.getUTF8Text())
//如需获取位置等其他结果请看文档


// project.json
{
  "abis": [
    "arm64-v8a",
    "armeabi-v7a",
    "x86",
    "x86_64"
  ],
  "assets": [
  ],
  "buildDir": "build",
  "build": {
    "build_id": null,
    "build_number": 0,
    "build_time": 0
  },
  "useFeatures": [],
  "icon": null,
  "ignoredDirs": [
    "build"
  ],
  "launchConfig": {
    "displaySplash": false,
    "hideLauncher": false,
    "hideLogs": false,
    "stableMode": false,
    "volumeUpcontrol": false,
    "permissions": [],
    "serviceDesc": "使脚本自动操作(点击、长按、滑动等)所需,若关闭则只能执行不涉及自动操作的脚本。",
    "splashIcon": null,
    "splashText": "Powered by TessOCR"
  },
  "libs": [
    "libtesseract.so",
    "libpng.so",
    "libleptonica.so",
    "libjpeg.so",
    "libjackpal-androidterm5.so",
    "libjackpal-termexec2.so"
  ],
  "main": "main.js",
  "name": "TessOCR",
  "outputPath": null,
  "packageName": "com.script.tessocr",
  "projectDirectory": null,
  "scripts": {},
  "signingConfig": {
    "alias": null,
    "keystore": null
  },
  "sourcePath": null,
  "versionCode": 1,
  "versionName": "1.0.0"
}

Web扩展与游戏编程

贪吃蛇

"ui";

ui.layout(
    <vertical>
        <canvas id="board" layout_weight="1"/>
        <relative h="120">
            <button id="up" text="↑" w="60" h="60" layout_alignParentTop="true" layout_centerHorizontal="true"/>
            <button id="left" text="←" w="60" h="60" layout_alignParentBottom="true" layout_toLeftOf="@id/down"/>
            <button id="down" text="↓" w="60" h="60" layout_alignParentBottom="true" layout_centerHorizontal="true"/>
            <button id="right" text="→" w="60" h="60" layout_alignParentBottom="true" layout_toRightOf="@id/down"/>
        </relative>
    </vertical>
);

//蛇的颜色
const SNAKE_COLOR = colors.parseColor("#4caf50");
//背景色
const BG_COLOR = colors.parseColor("#ffffff");
//苹果颜色
const APPLE_COLOR = colors.parseColor("#f44336");
//墙的颜色
const WALL_COLOR = colors.parseColor("#607d8b");
//文本颜色
const TEXT_COLOR =  colors.parseColor("#03a9f4");

//蛇自动移动的时间间隔,调小可以增加难度
const MOVE_INTERVAL = 500;
//方块宽度
const BLOCK_WIDTH = 40;
//游戏区域宽高
const GAME_BOARD_HEIGHT = 20;
const GAME_BOARD_WIDTH = 15;

//蛇的四个移动方向
const DIRECTION_LEFT = {x: -1, y: 0};
const DIRECTION_RIGHT = {x: 1, y: 0};
const DIRECTION_UP = {x: 0, y: -1};
const DIRECTION_DOWN = {x: 0, y: 1};

//蛇,是一个蛇身的坐标的数组
var snake = [{x: 4, y: 2}, {x: 3, y: 2}, {x: 2, y: 2}];
//苹果的坐标
var apple = generateApple();
//当前蛇的移动方向
var direction = DIRECTION_RIGHT;
//标记游戏是否结束
var isGameOver = false;
//分数
var score = 0;

var paint = new Paint();
ui.board.on("draw", function(canvas){
    //绘制背景色
    canvas.drawColor(BG_COLOR);
    //绘制分数
    paint.setColor(TEXT_COLOR);
    paint.setTextSize(50);
    canvas.drawText("分数: " + score, 30, 70, paint);
    //如果游戏结束则绘制游戏结束字样
    if(isGameOver){
        canvas.drawText("游戏结束!", canvas.getWidth() - 280, 70, paint);
    }
    //计算坐标偏移,是的游戏区域绘制在画面的水平居中位置
    var offset = {
        x: (canvas.getWidth() - (GAME_BOARD_WIDTH + 2) * BLOCK_WIDTH) / 2,
        y: 100
    };
    //偏移坐标
    canvas.translate(offset.x, offset.y);
    //绘制围墙
    paint.setColor(WALL_COLOR);
    for(var i = 0; i <= GAME_BOARD_WIDTH + 1; i++){
        //上围墙
        drawBlock(canvas, paint, i, 0);
        //下围墙
        drawBlock(canvas, paint, i, GAME_BOARD_HEIGHT + 1);
    }
    for(var i = 0; i <= GAME_BOARD_HEIGHT + 1; i++){
        //左围墙
        drawBlock(canvas, paint, 0, i);
        //右围墙
        drawBlock(canvas, paint, GAME_BOARD_WIDTH + 1, i);
    }
    //绘制蛇身
    paint.setColor(SNAKE_COLOR);
    for(var i = 0; i < snake.length; i++){
        drawBlock(canvas, paint, snake[i].x, snake[i].y);
    }
    //绘制苹果
    paint.setColor(APPLE_COLOR);
    drawBlock(canvas, paint, apple.x, apple.y);
});

//启动游戏线程
var gameThread = threads.start(game);

//按键点击时改变蛇的移动方向
ui.left.on("click", ()=> direction = DIRECTION_LEFT);
ui.right.on("click", ()=> direction = DIRECTION_RIGHT);
ui.up.on("click", ()=> direction = DIRECTION_UP);
ui.down.on("click", ()=> direction = DIRECTION_DOWN);


function game(){
    //每隔一段时间让蛇自动前进
    setInterval(()=>{
        move(direction.x, direction.y);
    }, MOVE_INTERVAL);
}

function move(dx, dy){
    log("move: %d, %d", dx, dy);
    direction.x = dx;
    direction.y = dy;
    //蛇前进时把一个新的方块添加到蛇头前面
    var head = snake[0];
    snake.splice(0, 0, {
        x: head.x + dx,
        y: head.y + dy
    });
    //如果蛇头吃到了苹果
    if(snakeEatsApple()){
        //添加分数和重新生成苹果
        score += 5;
        apple = generateApple();
    }else{
        //没有吃到苹果的情况下把蛇尾去掉保持蛇身长度不变
        snake.pop();
    }
    //碰撞检测
    collisionTest();
}

function snakeEatsApple(){
    return snake[0].x == apple.x && snake[0].y == apple.y;
}

function generateApple(){
    //循环生成苹果直至苹果不会生成在蛇身上
    var x, y;
    do{
        x = random(1, GAME_BOARD_WIDTH);
        y = random(1, GAME_BOARD_HEIGHT);
    }while(!isAppleValid(x, y));
    return {x: x, y: y};
}

function isAppleValid(x, y){
    for (var i = 0; i < snake.length; i++) {
        if (snake[i].x == x && snake[i].y == y) {
            return false;
        }
    }
    return true;
}

function collisionTest(){
    //检测蛇有没有撞到墙上
    var head = snake[0];
    if(head.x < 1 || head.x > GAME_BOARD_WIDTH
        || head.y < 1 || head.y > GAME_BOARD_HEIGHT){
            gameOver();
            return;
    }
    //检测蛇有没有撞到自己
    for(var i = 1; i < snake.length; i++){
        if(snake[i].x == head && snake[i].y == head){
            gameOver();
            return;
        }
    }
}

function gameOver(){
    gameThread.interrupt();
    isGameOver = true;
}

function drawBlock(canvas, paint, x, y){
    x *= BLOCK_WIDTH;
    y *= BLOCK_WIDTH;
    canvas.drawRect(x, y, x + BLOCK_WIDTH, y + BLOCK_WIDTH, paint);
}

贪吃蛇重力版

"ui";

ui.layout(
    <vertical>
        <canvas id="board" layout_weight="1"/>
        <relative h="120">
            <button id="up" text="↑" w="60" h="60" layout_alignParentTop="true" layout_centerHorizontal="true"/>
            <button id="left" text="←" w="60" h="60" layout_alignParentBottom="true" layout_toLeftOf="@id/down"/>
            <button id="down" text="↓" w="60" h="60" layout_alignParentBottom="true" layout_centerHorizontal="true"/>
            <button id="right" text="→" w="60" h="60" layout_alignParentBottom="true" layout_toRightOf="@id/down"/>
        </relative>
    </vertical>
);

//蛇的颜色
const SNAKE_COLOR = colors.parseColor("#4caf50");
//背景色
const BG_COLOR = colors.parseColor("#ffffff");
//苹果颜色
const APPLE_COLOR = colors.parseColor("#f44336");
//墙的颜色
const WALL_COLOR = colors.parseColor("#607d8b");
//文本颜色
const TEXT_COLOR =  colors.parseColor("#03a9f4");

//蛇自动移动的时间间隔,调小可以增加难度
const MOVE_INTERVAL = 500;
//方块宽度
const BLOCK_WIDTH = 40;
//游戏区域宽高
const GAME_BOARD_HEIGHT = 20;
const GAME_BOARD_WIDTH = 15;

//蛇的四个移动方向
const DIRECTION_LEFT = {x: -1, y: 0};
const DIRECTION_RIGHT = {x: 1, y: 0};
const DIRECTION_UP = {x: 0, y: -1};
const DIRECTION_DOWN = {x: 0, y: 1};

//蛇,是一个蛇身的坐标的数组
var snake = [{x: 4, y: 2}, {x: 3, y: 2}, {x: 2, y: 2}];
//苹果的坐标
var apple = generateApple();
//当前蛇的移动方向
var direction = DIRECTION_RIGHT;
//标记游戏是否结束
var isGameOver = false;
//分数
var score = 0;

var paint = new Paint();
ui.board.on("draw", function(canvas){
    //绘制背景色
    canvas.drawColor(BG_COLOR);
    //绘制分数
    paint.setColor(TEXT_COLOR);
    paint.setTextSize(50);
    canvas.drawText("分数: " + score, 30, 70, paint);
    //如果游戏结束则绘制游戏结束字样
    if(isGameOver){
        canvas.drawText("游戏结束!", canvas.getWidth() - 280, 70, paint);
    }
    //计算坐标偏移,是的游戏区域绘制在画面的水平居中位置
    var offset = {
        x: (canvas.getWidth() - (GAME_BOARD_WIDTH + 2) * BLOCK_WIDTH) / 2,
        y: 100
    };
    //偏移坐标
    canvas.translate(offset.x, offset.y);
    //绘制围墙
    paint.setColor(WALL_COLOR);
    for(var i = 0; i <= GAME_BOARD_WIDTH + 1; i++){
        //上围墙
        drawBlock(canvas, paint, i, 0);
        //下围墙
        drawBlock(canvas, paint, i, GAME_BOARD_HEIGHT + 1);
    }
    for(var i = 0; i <= GAME_BOARD_HEIGHT + 1; i++){
        //左围墙
        drawBlock(canvas, paint, 0, i);
        //右围墙
        drawBlock(canvas, paint, GAME_BOARD_WIDTH + 1, i);
    }
    //绘制蛇身
    paint.setColor(SNAKE_COLOR);
    for(var i = 0; i < snake.length; i++){
        drawBlock(canvas, paint, snake[i].x, snake[i].y);
    }
    //绘制苹果
    paint.setColor(APPLE_COLOR);
    drawBlock(canvas, paint, apple.x, apple.y);
});

//启动游戏线程
var gameThread = threads.start(game);

//按键点击时改变蛇的移动方向
ui.left.on("click", ()=> direction = DIRECTION_LEFT);
ui.right.on("click", ()=> direction = DIRECTION_RIGHT);
ui.up.on("click", ()=> direction = DIRECTION_UP);
ui.down.on("click", ()=> direction = DIRECTION_DOWN);


function game(){
    //每隔一段时间让蛇自动前进
    setInterval(()=>{
        move(direction.x, direction.y);
    }, MOVE_INTERVAL);
}

function move(dx, dy){
    log("move: %d, %d", dx, dy);
    direction.x = dx;
    direction.y = dy;
    //蛇前进时把一个新的方块添加到蛇头前面
    var head = snake[0];
    snake.splice(0, 0, {
        x: head.x + dx,
        y: head.y + dy
    });
    //如果蛇头吃到了苹果
    if(snakeEatsApple()){
        //添加分数和重新生成苹果
        score += 5;
        apple = generateApple();
    }else{
        //没有吃到苹果的情况下把蛇尾去掉保持蛇身长度不变
        snake.pop();
    }
    //碰撞检测
    collisionTest();
}

function snakeEatsApple(){
    return snake[0].x == apple.x && snake[0].y == apple.y;
}

function generateApple(){
    //循环生成苹果直至苹果不会生成在蛇身上
    var x, y;
    do{
        x = random(1, GAME_BOARD_WIDTH);
        y = random(1, GAME_BOARD_HEIGHT);
    }while(!isAppleValid(x, y));
    return {x: x, y: y};
}

function isAppleValid(x, y){
    for (var i = 0; i < snake.length; i++) {
        if (snake[i].x == x && snake[i].y == y) {
            return false;
        }
    }
    return true;
}

function collisionTest(){
    //检测蛇有没有撞到墙上
    var head = snake[0];
    if(head.x < 1 || head.x > GAME_BOARD_WIDTH
        || head.y < 1 || head.y > GAME_BOARD_HEIGHT){
            gameOver();
            return;
    }
    //检测蛇有没有撞到自己
    for(var i = 1; i < snake.length; i++){
        if(snake[i].x == head && snake[i].y == head){
            gameOver();
            return;
        }
    }
}

function gameOver(){
    gameThread.interrupt();
    isGameOver = true;
}

function drawBlock(canvas, paint, x, y){
    x *= BLOCK_WIDTH;
    y *= BLOCK_WIDTH;
    canvas.drawRect(x, y, x + BLOCK_WIDTH, y + BLOCK_WIDTH, paint);
}

AutoX注入webviwe

"ui";
ui.layout(
    <vertical>
        <horizontal bg="#c7edcc" gravity="center" h="auto">
            <button text="网络冲浪" id="surfInternetBtn" style="Widget.AppCompat.Button.Colored" w="auto" />
            <button text="记忆翻牌" id="loadLocalHtmlBtn" style="Widget.AppCompat.Button.Colored" w="auto" />
            <button text="控制台" id="consoleBtn" style="Widget.AppCompat.Button.Colored" w="auto" />
        </horizontal>
        <vertical h="*" w="*">
            <webview id="webView" layout_below="title" w="*" h="*" />
        </vertical>
    </vertical>
);

function calljavascript(webViewWidget, script, callback) {
    try {
        console.assert(webViewWidget != null, "webView控件为空");
        //console.log(script.toString())
        webViewWidget.evaluatejavascript("javascript:" + script, new JavaAdapter(android.webkit.ValueCallback, {
            onReceiveValue: (val) => {
                if (callback) {
                    callback(val);
                }
            }
        }));
    } catch (e) {
        console.error("执行javascript失败");
        console.trace(e);
    }
}

function AutoX() {
    let getAutoXFrame = () => {
        let bridgeFrame = document.getElementById("AutoXFrame");
        if (!bridgeFrame) {
            bridgeFrame = document.createElement('iframe');
            bridgeFrame.id = "AutoXFrame";
            bridgeFrame.style = "display: none";
            document.body.append(bridgeFrame);
        }
        return bridgeFrame;
    };
    const h5Callbackers = {};
    let h5CallbackIndex = 1;
    let setCallback = (callback) => {
        let callId = h5CallbackIndex++;
        h5Callbackers[callId] = {
            "callback": callback
        };
        return callId;
    };
    let getCallback = (callId) => {
        let callback = h5Callbackers[callId];
        if (callback) {
            delete h5Callbackers[callId];
        }
        return callback;
    };

    function invoke(cmd, params, callback) {
        let callId = null;
        try {
            let paramsStr = JSON.stringify(params);
            let AutoXFrame = getAutoXFrame();
            callId = setCallback(callback);
            AutoXFrame.src = "jsbridge://" + cmd + "/" + callId + "/" + encodeURIComponent(paramsStr);
        } catch (e) {
            if (callId) {
                getCallback(callId);
            }
            console.trace(e);
        }
    };
    let callback = (data) => {
        let callId = data.callId;
        let params = data.params;
        let callbackFun = getCallback(callId);
        if (callbackFun && callbackFun.callback) {
            callbackFun.callback(params);
        }
    };
    return {
        invoke: invoke,
        callback: callback
    };
};
function bridgeHandler_handle(cmd, params) {
    console.log('bridgeHandler处理 cmd=%s, params=%s', cmd, JSON.stringify(params));
    let fun = this[cmd];
    if (!fun) {
        throw new Error("cmd= " + cmd + " 没有定义实现");
    }
    let ret = fun(params)
    return ret;
}
function mFunction(params) {
    toastLog(params.toString());
    device.vibrate(120);
    return files.isDir('/storage/emulated/0/Download')//'toast提示成功';
}
function webViewExpand_init(webViewWidget) {
    webViewWidget.webViewClient = new JavaAdapter(android.webkit.WebViewClient, {
        onPageFinished: (webView, curUrl) => {
            try {
                // 注入 AutoX
                calljavascript(webView, AutoX.toString() + ";var auto0 = AutoX();auto0.invoke('mFunction','This is AutoX!',(data) => {console.log('接收到callback1:' + JSON.stringify(data));});", null);
            } catch (e) {
                console.trace(e)
            }
        },
        shouldOverrideUrlLoading: (webView, request) => {
            let url = '';
            try {
                url = (request.a && request.a.a) || (request.url);
                if (url instanceof android.net.Uri) {
                    url = url.toString();
                }
                if (url.indexOf("jsbridge://") == 0) {
                    let uris = url.split("/");
                    let cmd = uris[2];
                    let callId = uris[3];
                    let params = java.net.URLDecoder.decode(uris[4], "UTF-8");
                    console.log('AutoX处理javascript调用请求: callId=%s, cmd=%s, params=%s', callId, cmd, params);
                    let result = null;
                    try {
                        result = bridgeHandler_handle(cmd, JSON.parse(params));
                    } catch (e) {
                        console.trace(e);
                        result = {
                            message: e.message
                        };
                    }
                    result = result || {};
                    webView.loadUrl("javascript:auto0.callback({'callId':" + callId + ", 'params': " + JSON.stringify(result) + "});");
                } else if (url.startsWith("http://") || url.startsWith("https://") || url.startsWith("file://") || url.startsWith("ws://") || url.startsWith("wss://")) {
                    webView.loadUrl(url);
                } else {
                }
                return true;
            } catch (e) {
                if (e.javaException instanceof android.content.ActivityNotFoundException) {
                    webView.loadUrl(url);
                } else {
                    toastLog('无法打开URL: ' + url);
                }
                console.trace(e);
            }
        },
        onReceivedError: (webView, webResourceRequest, webResourceError) => {
            let url = webResourceRequest.getUrl();
            let errorCode = webResourceError.getErrorCode();
            let description = webResourceError.getDescription();
            console.trace(errorCode + " " + description + " " + url);
        }
    });
    webViewWidget.webChromeClient = new JavaAdapter(android.webkit.WebChromeClient, {
        onConsoleMessage: (msg) => {
            console.log("[%s:%s]: %s", msg.sourceId(), msg.lineNumber(), msg.message());
        }
    });
}
webViewExpand_init(ui.webView)
ui.webView.loadUrl("https://wht.im");

ui.surfInternetBtn.on("click", () => {
    webViewExpand_init(ui.webView);
    ui.webView.loadUrl("https://wht.im");
});
ui.consoleBtn.on("click", () => {
    app.startActivity("console");
});
ui.loadLocalHtmlBtn.on('click', () => {
    webViewExpand_init(ui.webView);
    let path = "file:" + files.path("game.html");
    ui.webView.loadUrl(path);
});

WebSocket

创建客户端

importPackage(Packages["okhttp3"]);
var client = new OkHttpClient.Builder().retryOnConnectionFailure(true).build();

var request = new Request.Builder().url("ws://192.168.31.164:9317").build();
client.dispatcher().cancelAll();//清理一次
myListener = {
    onOpen: function (webSocket, response) {
        print("onOpen");
        var json = {};
        json.type="hello";
        json.data= {device_name:"模拟设备",client_version:123,app_version:123,app_version_code:"233"};
        var hello=JSON.stringify(json);
        webSocket.send(hello);
    },
    onMessage: function (webSocket, msg) {
        print("msg");
        print(msg);
    },
    onClosing: function (webSocket, code, reason) {
        print("正在关闭");
    },
    onClosed: function (webSocket, code, reason) {
        print("关闭");
    },
    onFailure: function (webSocket, t, response) {
        print("错误");
        print( t);
    }
}
var webSocket= client.newWebSocket(request, new WebSocketListener(myListener));

setInterval(() => {

}, 1000);


WebSocket示例

// 新建一个WebSocket
// 指定web socket的事件回调在当前线程(好处是没有多线程问题要处理,坏处是不能阻塞当前线程,包括死循环)
// 不加后面的参数则回调在IO线程
let ws = web.newWebSocket("wss://demo.piesocket.com/v3/channel_1?notify_self", {
    eventThread: 'this'
});
console.show();
// 监听他的各种事件
ws.on("open", (res, ws) => {
    log("WebSocket已连接");
}).on("failure", (err, res, ws) => {
    log("WebSocket连接失败");
    console.error(err);
}).on("closing", (code, reason, ws) => {
    log("WebSocket关闭中");
}).on("text", (text, ws) => {
    console.info("收到文本消息: ", text);
}).on("binary", (bytes, ws) => {
    console.info("收到二进制消息:");
    console.info("hex: ", bytes.hex());
    console.info("base64: ", bytes.base64());
    console.info("md5: ", bytes.md5());
    console.info("size: ", bytes.size());
    console.info("bytes: ", bytes.toByteArray());
}).on("closed", (code, reason, ws) => {
    log("WebSocket已关闭: code = %d, reason = %s", code, reason);
});

// 发送文本消息
log("发送消息: Hello, WebSocket!");
ws.send("h");
setTimeout(() => {
    // 两秒后发送二进制消息
   log("发送二进制消息: 5piO5aSp5L2g6IO96ICDMTAw5YiG44CC");
   ws.send(web.ByteString.decodeBase64("5piO5aSp5L2g6IO96ICDMTAw5YiG44CC"));
}, 2000);
setTimeout(() => {
    // 8秒后断开WebSocket
    log("断开WebSocket");
    // 1000表示正常关闭
    ws.close(1000, null);
}, 8000);
setTimeout(() => {
    log("退出程序");
}, 12000)

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值