Auto.js 入门指南(十六)与第三方应用集成

481 阅读12分钟

前言

大家好,我是鲫小鱼。是一名不写前端代码的前端工程师,热衷于分享非前端的知识,带领切图仔逃离切图圈子,欢迎关注我,微信公众号:《鲫小鱼不正经》。欢迎点赞、收藏、关注,一键三连!!

第十六章:与第三方应用集成


一、理论讲解:Auto.js 与第三方应用交互的原理与方式

1.1 为什么需要与第三方应用集成?

在自动化脚本中,仅限于操作当前应用或 Auto.js 自身功能是远远不够的。很多自动化场景需要与系统服务、其他应用(如微信、支付宝、短信、浏览器、文件管理器等)进行深度交互,例如:

  • 消息通知:自动发送消息给微信/QQ好友或群组,发送短信、邮件。
  • 支付场景:唤起支付宝/微信支付,完成自动支付或查询订单。
  • 数据共享:在不同应用间传递数据(如从浏览器复制内容到笔记应用)。
  • 功能扩展:利用其他应用提供的能力(如相册选择图片、调用地图导航)。
  • 跨应用自动化:从一个应用跳转到另一个应用执行特定操作。

1.2 Android 应用间通信机制 (Intent、Activity、BroadcastReceiver)

Auto.js 作为运行在 Android 平台上的 JavaScript 引擎,可以通过调用 Android 原生 API 来实现与第三方应用的集成。核心机制包括:

  • Intent (意图):Android 中用于组件间通信的消息对象。可以用于启动 Activity(界面)、Service(后台服务)、BroadcastReceiver(广播)。Intent 可以携带数据,是应用间交互的主要方式。
    • 显式 Intent:明确指定目标组件(包名+类名)。
    • 隐式 Intent:通过 Action (动作) 和 Category (类别) 匹配目标组件,由系统选择最合适的应用处理。
  • Activity (活动):应用程序界面的基本组件。Intent 可以用来启动其他应用的 Activity。
  • BroadcastReceiver (广播接收器):用于接收系统或应用发送的广播消息。Auto.js 可以发送广播。
  • ContentProvider (内容提供者):用于在应用间共享数据(如联系人、短信)。
  • AccessibilityService (无障碍服务):Auto.js 的核心能力,通过模拟用户操作(点击、滑动、输入等)实现 UI 自动化,这是最常用的集成方式。

1.3 Auto.js 集成第三方应用的主要方法

  • UI 自动化操作:通过 text(), id(), className(), desc() 等查找控件并模拟点击、输入、滑动。这是最通用也最直接的方式,但对 UI 变化敏感。
  • app 模块 (API 封装):Auto.js 提供了 app 模块,封装了常用的应用操作 API,如 app.launchApp(), app.openUrl(), app.sendSms(), app.viewFile() 等,方便直接调用。
  • context.startActivity(intent) (原生 Intent):最灵活、强大的方式,可以直接构造 Android Intent 对象,启动任何支持 Intent 的 Activity 或 Service,并传递复杂数据。
  • 调用 shell 命令:通过 shell 命令执行系统级别的操作,如 am (Activity Manager) 命令启动应用或 Activity。
  • 利用公共接口/API:某些应用会开放 HTTP API 或 SDK 供开发者集成,Auto.js 可以通过 http 模块进行网络请求。

1.4 权限与兼容性问题

  • 权限:与第三方应用集成通常需要额外的权限,如发送短信、读取联系人、悬浮窗、无障碍服务等。需要确保脚本拥有必要的权限。
  • 兼容性:不同 Android 版本、不同手机品牌、不同应用版本都可能导致集成方式的差异。例如,应用的 UI 变化可能导致控件查找失败,高版本 Android 对后台启动应用、文件访问等有更严格的限制。
  • App 内部检测:部分 App 会检测自动化工具,可能导致脚本失效甚至封号风险。

二、代码示例:Auto.js 与第三方应用集成全场景实战

2.1 启动第三方应用 (包名/应用名)

// 方式一:通过包名启动 (推荐,精确)
// 获取微信包名:在Auto.js的"App信息"里查看,或用adb shell pm list packages -f | grep weixin
var wechatPackage = "com.tencent.mm";
if (app.launch(wechatPackage)) {
    toast("微信已启动");
    sleep(3000);
    // 启动后可以继续进行UI自动化操作
    // 例如:click("通讯录");
} else {
    toast("微信启动失败,可能未安装或包名错误");
}

// 方式二:通过应用名启动 (可能不准确,有同名应用风险)
if (app.launchApp("支付宝")) {
    toast("支付宝已启动");
} else {
    toast("支付宝启动失败");
}

2.2 发送短信、打电话、打开网页

这些是 app 模块提供的便捷功能。

// 发送短信
app.sendSms("10086", "查询话费");
toast("短信已发送到短信应用");

// 打电话
app.call("10010");
toast("已跳转到拨号界面");

// 打开网页
app.openUrl("https://www.baidu.com");
toast("已用浏览器打开百度");

// 访问文件 (这里会弹出一个文件选择器或文件管理器)
app.viewFile("/sdcard/test.txt");
toast("已尝试打开文件");

2.3 构造并发送复杂 Intent

这是 Auto.js 与第三方应用深度集成的核心,允许传递各种数据类型和标志。

// 引入必要的 Java 类
var Intent = Java.type("android.content.Intent");
var Uri = Java.type("android.net.Uri");

// 示例1:启动微信扫一扫 (显式Intent,指定包名和类名)
function launchWechatScan() {
    var intent = new Intent();
    intent.setClassName("com.tencent.mm", "com.tencent.mm.plugin.scanner.ui.BaseScanUI");
    // 或者指定Action来启动,如果知道的话
    // intent.setAction("com.tencent.mm.action.WX_SCAN");
    try {
        context.startActivity(intent);
        toast("已尝试启动微信扫一扫");
    } catch (e) {
        log("启动微信扫一扫失败: " + e, "ERROR");
        toast("启动失败,请检查微信版本或权限");
    }
}
// launchWechatScan();

// 示例2:跳转到支付宝付款码界面 (隐式Intent,通过Uri)
function launchAlipayPaymentCode() {
    var intent = new Intent(Intent.ACTION_VIEW);
    intent.setData(Uri.parse("alipayqr://platformapi/startapp?saId=10000007"));
    // 添加 FLAG_ACTIVITY_NEW_TASK,确保在非Activity上下文也能启动
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    try {
        context.startActivity(intent);
        toast("已尝试启动支付宝付款码");
    } catch (e) {
        log("启动支付宝付款码失败: " + e, "ERROR");
        toast("启动失败,请检查支付宝是否安装");
    }
}
// launchAlipayPaymentCode();

// 示例3:分享文本到任意应用
function shareText(textToShare) {
    var intent = new Intent(Intent.ACTION_SEND);
    intent.setType("text/plain"); // 指定分享内容类型为纯文本
    intent.putExtra(Intent.EXTRA_TEXT, textToShare); // 放入要分享的文本
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // 兼容无界面启动
    try {
        // 创建一个选择器,让用户选择分享到哪个应用
        var chooserIntent = Intent.createChooser(intent, "分享到...");
        chooserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // 选择器也需要此Flag
        context.startActivity(chooserIntent);
        toast("已弹出分享选择器");
    } catch (e) {
        log("分享失败: " + e, "ERROR");
    }
}
// shareText("我正在使用Auto.js自动化脚本!");

// 示例4:安装 APK 文件
function installApk(apkPath) {
    var file = new java.io.File(apkPath);
    if (!file.exists()) {
        toast("APK文件不存在: " + apkPath);
        return;
    }
    var intent = new Intent(Intent.ACTION_VIEW);
    var MimeTypeMap = Java.type("android.webkit.MimeTypeMap");
    var type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(MimeTypeMap.getFileExtensionFromUrl(apkPath));

    // Android 7.0 (API 24) 及以上需要使用 FileProvider
    if (device.sdkInt >= 24) {
        var FileProvider = Java.type("androidx.core.content.FileProvider");
        // 替换你的应用包名
        var authority = context.getPackageName() + ".fileprovider";
        var uri = FileProvider.getUriForFile(context, authority, file);
        intent.setDataAndType(uri, type || "application/vnd.android.package-archive");
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // 授予临时读取URI权限
    } else {
        intent.setDataAndType(Uri.fromFile(file), type || "application/vnd.android.package-archive");
    }
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    try {
        context.startActivity(intent);
        toast("已尝试安装APK");
    } catch (e) {
        log("安装APK失败: " + e, "ERROR");
        toast("安装失败,请检查文件或权限");
    }
}
// installApk("/sdcard/Download/some_app.apk"); // 替换为实际APK路径

2.4 使用 shell 命令启动应用/Activity

这是一种更底层的方式,可以绕过一些 Intent 的限制,但需要 root 权限或特定设置。

// 启动微信主界面 (需要Root权限或者在无障碍服务中运行)
// 注意:shell命令的路径可能因设备而异
// var cmd = "am start -n com.tencent.mm/.ui.LauncherUI";
// shell(cmd, true); // true 表示需要 root 权限
// toast("尝试通过shell启动微信");

// 启动指定 Activity 并传递数据
// var cmd2 = "am start -n com.example.app/.MainActivity --es 'param1' 'value1'";
// shell(cmd2, true);

2.5 监听广播与发送广播

Auto.js 可以发送自定义广播,也可以监听系统或应用发送的广播(但监听其他应用广播通常需要特定权限或更复杂实现)。

// 发送自定义广播 (需在Auto.js或其他应用中注册接收器)
function sendCustomBroadcast() {
    var intent = new Intent("com.autojs.ACTION_CUSTOM_EVENT");
    intent.putExtra("data", "Hello from Auto.js Broadcast!");
    context.sendBroadcast(intent);
    toast("自定义广播已发送");
}
// sendCustomBroadcast();

// 监听系统广播 (例如:监听屏幕亮灭)
// Auto.js 脚本通常以服务形式运行,可以注册 BroadcastReceiver
// var filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
// var receiver = new Java.type("android.content.BroadcastReceiver")({
//     onReceive: function(context, intent) {
//         log("屏幕亮了!");
//     }
// });
// context.registerReceiver(receiver, filter);
// log("已注册屏幕亮屏广播接收器");

// 注意:在脚本结束时需要 unregisterReceiver 否则可能内存泄漏
// events.on("exit", function(){
//     context.unregisterReceiver(receiver);
//     log("广播接收器已注销");
// });

2.6 与 Web 服务集成 (HTTP API)

如果第三方应用提供 Web API,可以直接通过 http 模块进行交互。

// 示例:通过 API 发送消息到钉钉群 (假设有机器人Webhook)
function sendDingTalkMessage(content) {
    var webhookUrl = "https://oapi.dingtalk.com/robot/send?access_token=YOUR_ACCESS_TOKEN";
    var data = {
        "msgtype": "text",
        "text": {
            "content": content
        }
    };
    http.postJson(webhookUrl, data, function(res, err) {
        if (err) {
            log("发送钉钉消息失败: " + err, "ERROR");
        } else {
            log("发送钉钉消息结果: " + res.body.string(), "INFO");
        }
    });
}
// sendDingTalkMessage("Auto.js 自动化脚本运行通知!");

2.7 调用其他应用的 ContentProvider (读取联系人)

需要 android.permission.READ_CONTACTS 权限。

// 读取联系人 (需要 READ_CONTACTS 权限)
function readContacts() {
    var ContactsContract = Java.type("android.provider.ContactsContract");
    var cursor = context.getContentResolver().query(
        ContactsContract.Contacts.CONTENT_URI,
        null,
        null,
        null,
        null
    );
    if (cursor != null) {
        while (cursor.moveToNext()) {
            var id = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
            var name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
            log("联系人: ID = " + id + ", 姓名 = " + name);
        }
        cursor.close();
    } else {
        log("无法读取联系人,请检查权限。", "WARN");
    }
}
// readContacts();

三、实战项目:唤起微信自动发送消息

3.1 项目需求

开发一个 Auto.js 脚本,能够通过 Intent 直接唤起微信,并自动向指定好友或文件传输助手发送一条消息。这个项目将结合 Intent 和 UI 自动化,展示如何高效地与微信集成。

3.2 项目结构

autojs-wechat-integrator/
├── main.js                  # 主入口,调用发送消息模块
├── modules/
│   ├── wechatSender.js      # 负责唤起微信和发送消息的核心逻辑
│   └── logger.js            # 日志模块
├── logs/
│   └── wechat_log.log
└── README.md

3.3 logger.js 日志模块 (复用)

// modules/logger.js
function log(msg, level = "INFO") {
    let line = `[${new Date().toLocaleTimeString()}][${level}] ${msg}`;
    files.append("../logs/wechat_log.log", line + "\n");
    console.log(line); // 同时输出到控制台
}

module.exports = { log };

3.4 wechatSender.js 微信发送模块

// modules/wechatSender.js
const logger = require('./logger.js');

const WECHAT_PACKAGE = "com.tencent.mm";
const WECHAT_LAUNCHER_ACTIVITY = "com.tencent.mm.ui.LauncherUI";

/**
 * 启动微信并自动发送消息给指定联系人
 * @param {string} contactName 接收消息的联系人名称(或文件传输助手)
 * @param {string} message 要发送的消息内容
 * @returns {boolean} 是否成功发送
 */
function sendWechatMessage(contactName, message) {
    logger.log(`尝试向 '${contactName}' 发送消息:'${message}'`);

    // 1. 启动微信
    logger.log("启动微信...");
    if (!app.launch(WECHAT_PACKAGE)) {
        logger.log("微信启动失败,请检查是否安装。", "ERROR");
        toast("微信启动失败");
        return false;
    }

    // 等待微信主界面加载
    waitForActivity(WECHAT_LAUNCHER_ACTIVITY, 5000);
    sleep(2000); // 等待界面稳定

    // 2. 进入搜索界面
    logger.log("进入微信搜索...");
    let searchBtn = id("com.tencent.mm:id/fsl").findOne(3000) || desc("搜索").findOne(3000); // 微信搜索按钮ID可能变化
    if (!searchBtn) {
        logger.log("未找到微信搜索按钮。", "ERROR");
        toast("未找到搜索按钮");
        return false;
    }
    searchBtn.click();
    sleep(1500);

    // 3. 输入联系人名称并选择
    logger.log(`搜索联系人:'${contactName}'`);
    let searchInput = id("com.tencent.mm:id/fsk").findOne(3000) || className("android.widget.EditText").desc("搜索").findOne(3000);
    if (!searchInput) {
        logger.log("未找到搜索输入框。", "ERROR");
        toast("未找到搜索框");
        return false;
    }
    searchInput.setText(contactName);
    sleep(2000); // 等待搜索结果

    let contactItem = text(contactName).findOne(3000);
    if (!contactItem) {
        logger.log(`未找到联系人:'${contactName}'`, "ERROR");
        toast("未找到联系人");
        // 清除搜索框内容并退出
        id("com.tencent.mm:id/fsl").click(); // 返回到主界面
        return false;
    }
    contactItem.click();
    sleep(2000); // 等待进入聊天界面

    // 4. 输入消息并发送
    logger.log(`输入消息:'${message}' 并发送...`);
    let messageInput = id("com.tencent.mm:id/fsk").findOne(3000) || className("android.widget.EditText").desc("输入").findOne(3000);
    if (!messageInput) {
        logger.log("未找到消息输入框。", "ERROR");
        toast("未找到消息框");
        return false;
    }
    messageInput.setText(message);
    sleep(500);

    let sendBtn = id("com.tencent.mm:id/fss").findOne(2000) || text("发送").findOne(2000);
    if (!sendBtn) {
        logger.log("未找到发送按钮。", "ERROR");
        toast("未找到发送按钮");
        return false;
    }
    sendBtn.click();
    sleep(1000);

    logger.log("消息发送成功!", "INFO");
    toast("消息发送成功");
    back(); // 返回到主界面
    sleep(500);
    home(); // 返回主页
    return true;
}

module.exports = { sendWechatMessage };

3.5 main.js 主入口脚本

// main.js
const wechatSender = require('./modules/wechatSender.js');
const logger = require('./modules/logger.js');

logger.log("脚本启动:准备发送微信消息...");

// 示例:发送消息给文件传输助手
wechatSender.sendWechatMessage("文件传输助手", "Hello, Auto.js! 这是我的第十六章节示例。");

// 示例:发送消息给特定好友 (请替换为你的好友名称)
// wechatSender.sendWechatMessage("你的好友名称", "测试消息:今天的签到任务已完成。");

logger.log("所有微信消息发送任务已完成。");

3.6 运行效果与分析

  • 运行 main.js 脚本,它将自动启动微信。
  • 脚本会模拟点击搜索按钮,输入联系人名称,选择联系人进入聊天界面。
  • 然后,脚本会自动输入预设的消息内容,并点击发送按钮。
  • 整个过程通过日志进行详细记录,方便追踪和调试。
  • 这个实战项目展示了如何结合 app 模块的启动功能和 UI 自动化来与复杂的第三方应用(如微信)进行交互。需要注意的是,微信的 UI 控件 ID 和文本可能会随版本更新而变化,因此脚本需要定期维护。

四、常见问题与解决方案

问题解决方案
应用无法启动/闪退检查包名/类名是否正确;检查权限是否完整;应用版本兼容性;是否开启无障碍服务。
控件找不到/点击无效使用 UI Automator Viewer 或 Auto.js Pro 布局分析器重新定位控件;增加等待时间;尝试多种查找方式(text/desc/id/className)。
Intent 调用失败/无响应检查 Intent Action/Category/Data 格式是否正确;添加 FLAG_ACTIVITY_NEW_TASK;检查目标应用是否支持此 Intent。
文件访问权限问题Android 6.0+ 需要动态申请存储权限;Android 11+ (API 30+) 需要适配分区存储 (Scoped Storage)。
后台操作被系统杀死考虑使用 Auto.js 的前台服务或定时任务,确保脚本在后台稳定运行。
应用 UI 更新导致脚本失效定期更新脚本以适配应用 UI 变化;尝试使用更通用的查找方式(如模糊查找、多条件查找)。
第三方应用反自动化/检测增加随机延时;模拟更真实的用户操作;使用图像识别/OCR 规避控件检测;避免频繁操作。
数据共享/文件传输失败检查文件路径和权限;对于 Android 7.0+ 考虑使用 FileProvider。
shell 命令无权限执行确保设备已 Root,并授予 Auto.js Root 权限。
耗时操作阻塞主线程将耗时操作放入子线程 (threads.start()),避免 UI 卡顿或 ANR。

五、性能优化建议

  • 精确定位控件:优先使用 idtext 精确查找控件,减少遍历开销。在无法精确查找时,再考虑 descclassName 或结合 bounds
  • 合理设置等待时间:使用 waitFor...() 函数或 sleep() 时,根据实际情况设置合适的等待时间,避免过长或过短导致效率低下或操作失败。
  • 避免不必要的 UI 操作:能通过 Intent 或 API 完成的功能,优先使用 Intent 或 API,减少 UI 自动化操作。
  • 分批处理:如果需要处理大量数据或重复操作,考虑分批进行,减少单次操作的负担。
  • 日志分级:在生产环境中,降低日志输出等级,避免频繁的日志输出影响性能。
  • 资源释放:确保在脚本结束后及时释放资源(如关闭文件、数据库连接、停止线程)。
  • 异常捕获与重试:对关键操作进行异常捕获,并增加重试机制,提高脚本的健壮性。
  • 多线程优化:将与 UI 自动化无关的耗时操作(如网络请求、数据处理)放入子线程执行,避免阻塞主线程。
  • UI 适配:针对不同分辨率和 Android 版本进行兼容性测试和适配,提高脚本的通用性。
  • 模拟真实用户行为:引入随机延时、滑动速度变化等,使操作更接近真人,降低被反自动化检测的风险。

最后感谢阅读!欢迎关注我,微信公众号:《鲫小鱼不正经》。欢迎点赞、收藏、关注,一键三连!!!