2025-04-15 11:31:12 +08:00
|
|
|
|
# 与 Android(adb) 集成
|
2025-04-09 17:04:56 +08:00
|
|
|
|
|
|
|
|
|
Midscene 支持使用视觉语言(VL)模型来控制 Android 设备。
|
|
|
|
|
|
|
|
|
|
import { PackageManagerTabs } from '@theme';
|
|
|
|
|
|
|
|
|
|
:::info 样例项目
|
|
|
|
|
你可以在这里看到向 Android 集成的样例项目:[https://github.com/web-infra-dev/midscene-example/blob/main/android-demo](https://github.com/web-infra-dev/midscene-example/blob/main/android-demo)
|
|
|
|
|
|
|
|
|
|
这里还有一个 Android 和 Vitest 结合的样例项目:[https://github.com/web-infra-dev/midscene-example/tree/main/android-with-vitest-demo](https://github.com/web-infra-dev/midscene-example/tree/main/android-with-vitest-demo)
|
|
|
|
|
:::
|
|
|
|
|
|
|
|
|
|
## 准备工作
|
|
|
|
|
|
2025-04-15 11:31:12 +08:00
|
|
|
|
### 配置 API Key
|
2025-04-09 17:04:56 +08:00
|
|
|
|
|
2025-04-15 11:31:12 +08:00
|
|
|
|
配置 VL 模型的 API Key。如果使用 qwen-2.5-vl,可以配置如下:
|
2025-04-09 17:04:56 +08:00
|
|
|
|
|
|
|
|
|
```bash
|
2025-04-15 11:31:12 +08:00
|
|
|
|
OPENAI_BASE_URL="https://dashscope.aliyuncs.com/compatible-mode/v1"
|
|
|
|
|
OPENAI_API_KEY="......"
|
|
|
|
|
MIDSCENE_MODEL_NAME="qwen-vl-max-latest"
|
|
|
|
|
MIDSCENE_USE_QWEN_VL=1
|
2025-04-09 17:04:56 +08:00
|
|
|
|
```
|
|
|
|
|
|
2025-04-15 11:31:12 +08:00
|
|
|
|
控制 Android 设备时,你只能使用 VL 模型。更多详情请参考 [选择模型](./choose-a-model)。
|
|
|
|
|
|
2025-04-09 17:04:56 +08:00
|
|
|
|
### 准备 adb 环境
|
|
|
|
|
|
2025-04-15 11:31:12 +08:00
|
|
|
|
`adb` 是一个命令行工具,允许你与 Android 设备通信。
|
|
|
|
|
|
2025-04-09 17:04:56 +08:00
|
|
|
|
- 方式一:使用 [Android Studio](https://developer.android.com/studio?hl=zh-cn) 安装
|
|
|
|
|
- 方式二:使用 [Android command-line tools](https://developer.android.com/studio#command-line-tools-only) 安装
|
|
|
|
|
|
|
|
|
|
验证 adb 是否安装成功:
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
adb --version
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
当你看到如下输出,说明 adb 已经安装成功:
|
|
|
|
|
|
|
|
|
|
```log
|
|
|
|
|
Android Debug Bridge version 1.0.41
|
|
|
|
|
Version 34.0.4-10411341
|
|
|
|
|
Installed as /usr/local/bin//adb
|
|
|
|
|
Running on Darwin 24.3.0 (arm64)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 使用 adb 连接 Android 设备
|
|
|
|
|
|
2025-04-10 20:51:56 +08:00
|
|
|
|
在系统设置的开发者选项中,开启安卓设备的『USB 调试』,如果存在『USB 调试(安全设置)』,也需要开启,接着使用数据线连接一台安卓设备
|
|
|
|
|
|
|
|
|
|
<p align="center">
|
|
|
|
|
<img src="/android-usb-debug.png" alt="android usb debug" width="400"/>
|
|
|
|
|
</p>
|
2025-04-09 17:04:56 +08:00
|
|
|
|
|
|
|
|
|
通过下面命令确认已经连接:
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
adb devices -l
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
当你看到如下输出,说明连接成功:
|
|
|
|
|
|
|
|
|
|
```log
|
|
|
|
|
List of devices attached
|
|
|
|
|
s4ey59 device usb:34603008X product:cezanne model:M2006J device:cezan transport_id:3
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 第一步:安装依赖
|
|
|
|
|
|
|
|
|
|
<PackageManagerTabs command="install @midscene/android --save-dev" />
|
|
|
|
|
|
|
|
|
|
## 第二步:编写脚本
|
|
|
|
|
|
|
|
|
|
这里以使用安卓浏览器搜索耳机为例。(当然,你也可以使用设备上的其他任何应用)
|
|
|
|
|
|
|
|
|
|
编写下方代码,保存为 `./demo.ts`
|
|
|
|
|
|
|
|
|
|
```typescript title="./demo.ts"
|
|
|
|
|
import { AndroidAgent, AndroidDevice, getConnectedDevices } from '@midscene/android';
|
|
|
|
|
|
|
|
|
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
|
|
|
Promise.resolve(
|
|
|
|
|
(async () => {
|
|
|
|
|
const devices = await getConnectedDevices();
|
|
|
|
|
const page = new AndroidDevice(devices[0].udid);
|
|
|
|
|
|
|
|
|
|
// 👀 初始化 Midscene agent
|
|
|
|
|
const agent = new AndroidAgent(page,{
|
|
|
|
|
aiActionContext:
|
|
|
|
|
'如果出现位置、权限、用户协议等弹窗,点击同意。如果出现登录页面,关闭它。',
|
|
|
|
|
});
|
|
|
|
|
await page.connect();
|
|
|
|
|
|
|
|
|
|
// 👀 打开浏览器并导航到 ebay.com(请确保当前页面有浏览器 App 喔)
|
|
|
|
|
await agent.aiAction('open browser and navigate to ebay.com');
|
|
|
|
|
|
|
|
|
|
await sleep(5000);
|
|
|
|
|
|
|
|
|
|
// 👀 输入关键词,执行搜索
|
|
|
|
|
await agent.aiAction('在搜索框输入 "Headphones" ,敲回车');
|
|
|
|
|
|
|
|
|
|
// 👀 等待加载完成
|
|
|
|
|
await agent.aiWaitFor("页面中至少有一个耳机商品");
|
|
|
|
|
// 或者你也可以使用一个普通的 sleep:
|
|
|
|
|
// await sleep(5000);
|
|
|
|
|
|
|
|
|
|
// 👀 理解页面内容,提取数据
|
|
|
|
|
const items = await agent.aiQuery(
|
|
|
|
|
"{itemTitle: string, price: Number}[], 找到列表里的商品标题和价格"
|
|
|
|
|
);
|
|
|
|
|
console.log("耳机商品信息", items);
|
|
|
|
|
|
|
|
|
|
// 👀 用 AI 断言
|
|
|
|
|
await agent.aiAssert("界面左侧有类目筛选功能");
|
|
|
|
|
})()
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 第三步:运行
|
|
|
|
|
|
|
|
|
|
使用 `tsx` 来运行
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# run
|
|
|
|
|
npx tsx demo.ts
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
稍等片刻,你会看到如下输出:
|
|
|
|
|
|
|
|
|
|
```log
|
|
|
|
|
[
|
|
|
|
|
{
|
|
|
|
|
itemTitle: 'JBL Tour Pro 2 - True wireless Noise Cancelling earbuds with Smart Charging Case',
|
|
|
|
|
price: 551.21
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
itemTitle: 'Soundcore Space One无线耳机40H ANC播放时间2XStronger语音还原',
|
|
|
|
|
price: 543.94
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 第四步:查看运行报告
|
|
|
|
|
|
|
|
|
|
当上面的命令执行成功后,会在控制台输出:`Midscene - report file updated: /path/to/report/some_id.html`, 通过浏览器打开该文件即可看到报告。
|
|
|
|
|
|
|
|
|
|
## Android Agent 上的更多接口
|
|
|
|
|
|
|
|
|
|
除了 [API 参考](./API) 中的通用 Agent 接口,AndroidAgent 还提供了一些其他接口:
|
|
|
|
|
|
|
|
|
|
### `agent.launch()`
|
|
|
|
|
|
|
|
|
|
启动一个网页或原生页面。
|
|
|
|
|
|
|
|
|
|
* 类型
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
function launch(uri: string): Promise<void>;
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
* 参数:
|
|
|
|
|
* `uri: string` - 要打开的 uri
|
|
|
|
|
|
|
|
|
|
* 返回值:
|
|
|
|
|
* `Promise<void>`
|
|
|
|
|
|
|
|
|
|
* 示例:
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
import { AndroidAgent, AndroidDevice } from '@midscene/android';
|
|
|
|
|
|
|
|
|
|
const page = new AndroidDevice('s4ey59ytbitot4yp');
|
|
|
|
|
const agent = new AndroidAgent(page);
|
|
|
|
|
|
|
|
|
|
await agent.launch('https://www.ebay.com'); // 打开网页
|
|
|
|
|
await agent.launch('com.android.settings'); // 打开系统设置 app(package name)
|
|
|
|
|
await agent.launch('com.android.settings/.Settings'); // 打开系统设置 app(package name) 的 .Settings(activity name) 页面
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### `agentFromAdbDevice()`
|
|
|
|
|
|
|
|
|
|
从已连接的 adb 设备中,创建一个 AndroidAgent。
|
|
|
|
|
|
|
|
|
|
* 类型
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
function agentFromAdbDevice(deviceId?: string, opts?: PageAgentOpt): Promise<AndroidAgent>;
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
* 参数:
|
|
|
|
|
* `deviceId?: string` - 可选参数,要连接的 adb 设备 id,如果未传入,则使用第一个连接的设备
|
|
|
|
|
* `opts?: PageAgentOpt` - 可选参数,用于初始化 AndroidAgent 的配置,参考 [构造器](./API)
|
|
|
|
|
|
|
|
|
|
* 返回值:
|
|
|
|
|
* `Promise<AndroidAgent>` 返回一个 AndroidAgent 实例
|
|
|
|
|
|
|
|
|
|
* 示例:
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
import { agentFromAdbDevice } from '@midscene/android';
|
|
|
|
|
|
|
|
|
|
const agent = await agentFromAdbDevice('s4ey59ytbitot4yp'); // 传入 deviceId
|
|
|
|
|
const agent = await agentFromAdbDevice(); // 不传入 deviceId,则使用第一个连接的设备
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### `getConnectedDevices()`
|
|
|
|
|
|
|
|
|
|
获取所有连接的 Android 设备。
|
|
|
|
|
|
|
|
|
|
* 类型
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
function getConnectedDevices(): Promise<Device[]>;
|
|
|
|
|
interface Device {
|
|
|
|
|
/**
|
|
|
|
|
* The device udid.
|
|
|
|
|
*/
|
|
|
|
|
udid: string;
|
|
|
|
|
/**
|
|
|
|
|
* Current device state, as it is visible in
|
|
|
|
|
* _adb devices -l_ output.
|
|
|
|
|
*/
|
|
|
|
|
state: string;
|
|
|
|
|
port?: number;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
* 返回值:
|
|
|
|
|
* `Promise<Device[]>` 返回一个 Device 数组
|
|
|
|
|
|
|
|
|
|
* 示例:
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
import { agentFromAdbDevice, getConnectedDevices } from '@midscene/android'
|
|
|
|
|
|
|
|
|
|
const devices = await getConnectedDevices();
|
|
|
|
|
console.log(devices);
|
|
|
|
|
const agent = await agentFromAdbDevice(devices[0].udid);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 更多
|
|
|
|
|
|
|
|
|
|
* 更多 Agent 上的 API 接口请参考 [API 参考](./API)。
|
|
|
|
|
* 更多关于提示词的技巧请参考 [提示词技巧](./prompting-tips)
|
2025-04-10 20:51:56 +08:00
|
|
|
|
|
|
|
|
|
## FAQ
|
|
|
|
|
|
|
|
|
|
### 为什么我连接了设备,但是通过 adb 仍然无法控制?
|
|
|
|
|
|
|
|
|
|
请检查是否在系统设置的开发者选项中,如果存在『USB 调试(安全设置)』,也需要开启。
|
|
|
|
|
|
|
|
|
|
<p align="center">
|
|
|
|
|
<img src="/android-usb-debug.png" alt="android usb debug" width="400"/>
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
### 为什么我连接了设备,但是通过 `adb devices -l` 命令看不到设备?
|
|
|
|
|
|
|
|
|
|
[连接设备](./integrate-with-android#使用-adb-连接-android-设备) 时,请确保设备处于解锁状态。
|