01.局域网调试协议文档
本文档定义了 PC 端(客户端)与 Android 端(服务端)调试服务之间的文本通信协议。通信基于纯文本帧,格式通常为 指令:参数 或 纯指令。
一、 消息提示功能
1. 显示 Toast
在 Android 设备上显示一条提示信息。如果应用拥有悬浮窗权限,则显示悬浮窗 Toast,否则显示普通 Toast。
webSocket.send("toast:PC端连接成功");
二、 环境与配置功能
1. 设置项目路径
设置 Android 端的全局项目工作路径。
- 发送格式:setProjectPath:绝对路径
- Java 示例:
webSocket.send("setProjectPath:/sdcard/MyWorkspace/Project1");
三、 文件与目录操作功能
1. 删除文件或目录
请求删除指定的文件或文件夹。
- 发送格式:del:文件或目录绝对路径
- 接收格式:删除成功返回 delSuccess,删除失败返回 delFail
- Java 示例:
webSocket.send("del:/sdcard/temp.txt");
// 之后监听返回值
// 成功: "delSuccess"
// 失败: "delFail"
2. 创建文件
在指定路径创建一个空文件。
- 发送格式:touch:文件绝对路径
- 接收格式:创建成功返回 touchSuccess,失败返回 touchFail
- Java 示例:
webSocket.send("touch:/sdcard/new_file.txt");
3. 创建文件夹
递归创建目录。
- 发送格式:mkdir:目录绝对路径
- 接收格式:创建成功返回 mkdirSuccess,失败返回 mkdirFail
- Java 示例:
webSocket.send("mkdir:/sdcard/new_folder/sub_folder");
4. 文件重命名 (指定新名称)
在原文件所在的目录下修改文件名称。
- 发送格式:rename:原文件绝对路径###新名称
- 接收格式:成功返回 renameSuccess,失败返回 renameFail
- Java 示例:
webSocket.send("rename:/sdcard/old.txt###new.txt");
5. 文件移动或重命名 (指定目标路径)
将文件移动或重命名到指定的完整目标路径。
- 发送格式:renameTo:原文件绝对路径###目标完整路径
- 接收格式:成功返回 renameToSuccess,失败返回 renameToFail
- Java 示例:
webSocket.send("renameTo:/sdcard/old.txt###/sdcard/backup/new.txt");
6. 罗列目录文件
获取指定目录下的文件和文件夹列表。列表经过了特定规则排序:项目文件夹优先,其次普通目录,最后是文件(按 js、lua、xml、json、png、zip 及其他后缀优先级排序)。
- 发送格式:ls:目录绝对路径
- 接收格式:ls:是否是文件#是否是项目#名称#绝对路径_是否是文件#是否是项目#名称#绝对路径_...
- Java 示例:
webSocket.send("ls:/sdcard/");
// 接收示例数据解析:
// false#true#MyApp#/sdcard/MyApp_ (代表这是一个项目文件夹)
// true#false#main.js#/sdcard/main.js_ (代表这是一个js文件)
四、 文件下载功能
注意:下载功能采用流式传输,大文件会分块发送,客户端需监听进度和结束标志。分块大小固定为 1MB。
1. 下载文件或文件夹
请求 Android 端发送文件。如果指定的是文件夹,Android 端会自动将其压缩为 ZIP 后发送。
- 发送格式:downloadFile:文件或文件夹绝对路径
- 接收格式流:
- 开始标志:downloadFileStart:文件对象toString
- 数据块:downloadFile:Base64数据块
- 进度更新:downloadFileProgress:百分比数值 (如 45.50)
- 结束标志:downloadFileEnd:true (成功) 或 downloadFileEnd:false (失败)
- Java 示例:
webSocket.send("downloadFile:/sdcard/config.json");
// 在 onMessage 中处理流式数据
public void onMessage(String message) {
if (message.startsWith("downloadFileStart:")) {
// 开始接收,初始化文件流
} else if (message.startsWith("downloadFile:")) {
// 解析 Base64 并追加写入文件
String base64Chunk = message.substring("downloadFile:".length());
// 写入逻辑...
} else if (message.startsWith("downloadFileProgress:")) {
// 更新进度条
String progress = message.substring("downloadFileProgress:".length());
} else if (message.equals("downloadFileEnd:true")) {
// 接收完成,关闭文件流
} else if (message.equals("downloadFileEnd:false")) {
// 接收失败
}
}
2. 下载项目
请求下载当前配置的项目,传输机制与下载文件完全一致,仅指令前缀不同。
- 发送格式:downloadProject:项目标识或路径参数
- 接收格式流:将上述示例中的 downloadFile 替换为 downloadProject 即可。
五、 截屏与 UI 树获取功能
1. 获取截屏
获取当前 Android 屏幕的截图,以 Base64 编码返回。
- 发送格式:captureScreen
- 接收格式:screenShot:Base64图片数据
- Java 示例:
webSocket.send("captureScreen");
// 接收后解码
// String base64 = message.substring("screenShot:".length());
// byte[] imageBytes = Base64.getDecoder().decode(base64);
2. 获取截屏与节点信息
同时获取截图及 UI 控件节点信息。服务端优先获取 JSON 格式的节点信息,如果没有则获取 XML 格式。只有成功获取截屏后才会去获取节点信息。
- 发送格式:screenAndNodeInfo
- 接收格式:
- 带JSON节点:screenAndNodeInfo:Base64图片##JSON##JSON节点数据
- 带XML节点:screenAndNodeInfo:Base64图片##XML##XML节点数据
- Java 示例:
webSocket.send("screenAndNodeInfo");
String data = message.substring("screenAndNodeInfo:".length());
if (data.contains("##JSON##")) {
String[] parts = data.split("##JSON##");
String imageBase64 = parts[0];
String nodeJson = parts[1];
// 处理图片和JSON...
} else if (data.contains("##XML##")) {
String[] parts = data.split("##XML##");
String imageBase64 = parts[0];
String nodeXml = parts[1];
// 处理图片和XML...
}
六、 脚本执行控制功能
1. 运行指定路径的 JS 脚本
运行 Android 端指定路径下的 JS 文件。
- 发送格式:runJs:脚本绝对路径
- Java 示例:
webSocket.send("runJs:/sdcard/Project1/main.js");
2. 运行 JS 代码片段
直接发送代码字符串执行,并指定运行时的上下文路径。
- 发送格式:runJsCode:JS代码内容###上下文路径
- Java 示例:
String jsCode = "let app = getApp(); console.log(app.getVersion());";
String contextPath = "/sdcard/Project1";
webSocket.send("runJsCode:" + jsCode + "###" + contextPath);
3. 停止运行 JS
终止当前正在运行的 JavaScript 脚本。
webSocket.send("stopJs");
七、 剪贴板操作功能
注意:从安卓 12 开始允许设置剪切板为敏感数据,且后台操作剪切板可能受到系统限制。
1. 获取剪切板内容
读取 Android 设备当前的剪贴板文本。
- 发送格式:getClip
- 接收格式:clipContent:剪贴板文本内容
- Java 示例:
webSocket.send("getClip");
// String clipText = message.substring("clipContent:".length());
2. 设置剪切板内容
向 Android 设备的剪贴板写入文本。
- 发送格式:setClip:要复制的文本内容
- Java 示例:
webSocket.send("setClip:这是从PC端复制的配置代码");
八、 文件上传功能
注意:上传文件时,请将文件进行 Base64 编码后分块发送,避免单帧数据过大导致内存溢出。服务端接收到数据后会以追加模式写入文件。
1. 开始上传文件 (初始化)
通知 Android 端准备接收文件,服务端会自动创建父目录,如果存在同名文件则会将其删除。
- 发送格式:uploadFileStart:目标保存绝对路径
- Java 示例:
webSocket.send("uploadFileStart:/sdcard/upload/target.apk");
2. 传输文件数据块
发送文件的 Base64 编码分块数据。
- 发送格式:uploadFile:目标保存绝对路径###Base64数据块
- Java 示例:
String filePath = "/sdcard/upload/target.apk";
FileInputStream fis = new FileInputStream("local_file.apk");
byte[] buffer = new byte[1024 * 512]; // 建议 512KB 或 1MB 分块
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1) {
String base64Chunk = Base64.getEncoder().encodeToString(Arrays.copyOf(buffer, bytesRead));
webSocket.send("uploadFile:" + filePath + "###" + base64Chunk);
Thread.sleep(50); // 稍作延迟,防止将 Android 端缓冲区打满
}
fis.close();
九、 项目上传功能
项目上传采用先传 ZIP 包,再由 Android 端自动解压的机制。
1. 开始上传项目 (初始化)
通知 Android 端准备接收项目的 ZIP 压缩包,服务端会创建父目录并删除旧的同名 ZIP 文件。
- 发送格式:uploadProjectStart:ZIP包临时存储绝对路径.zip
- Java 示例:
webSocket.send("uploadProjectStart:/sdcard/temp/project.zip");
2. 传输项目 ZIP 数据块
发送项目 ZIP 包的 Base64 编码分块数据。
- 发送格式:uploadProject:ZIP包临时存储绝对路径.zip###Base64数据块
- Java 示例:
String zipPath = "/sdcard/temp/project.zip";
// 分块读取并发送逻辑与“传输文件数据块”完全一致
String base64Chunk = Base64.getEncoder().encodeToString(zipChunkBytes);
webSocket.send("uploadProject:" + zipPath + "###" + base64Chunk);
3. 结束上传项目 (触发解压)
通知 Android 端项目 ZIP 包已发送完毕。Android 端会等待文件就绪,执行解压,删除临时 ZIP 文件,并返回准备就绪信号。
- 发送格式:uploadProjectEnd:成功状态###ZIP包临时存储绝对路径.zip
- 接收格式:preparedRunCode
- Java 示例:
String zipPath = "/sdcard/temp/project.zip";
webSocket.send("uploadProjectEnd:true###" + zipPath);
// 等待服务端解压完成
// 收到 "preparedRunCode" 后,即可安全地发送 runJs 指令让 Android 端执行项目代码
十、 服务端主动推送日志功能
服务端在执行各项操作时,会主动向客户端推送不同级别的日志信息,便于 PC 端在控制台展示。
- 推送格式:
- 详细日志:log_v:日志内容
- 信息日志:log_i:日志内容
- 调试日志:log_d:日志内容
- 警告日志:log_w:日志内容
- 错误日志:log_e:日志内容
- Java 示例:
public void onMessage(String message) {
if (message.startsWith("log_e:")) {
// 提取错误信息并在 PC 端控制台标红显示
String errLog = message.substring("log_e:".length());
System.err.println("[Android Error] " + errLog);
} else if (message.startsWith("log_i:")) {
String infoLog = message.substring("log_i:".length());
System.out.println("[Android Info] " + infoLog);
}
// 其他级别同理处理...
}
十一、远程调试
如果使用的是远程调试,则需要在传输指令之前添加标识和对方设备号码。
在远程调试中,PC端和Android移动端都属于客户端,远程服务器需要部署在自己的服务器中。
1.PC端给移动端发指令
pc2phone#移动端号码#指令
例如:
pc2phone#15224789963#toast:PC端链接成功
2.移动端给PC端发指令
phone2pc#PC端号码#指令
例如:
phone2pc#25638985465#toast:移动端端链接成功
其他的所有指令都是按照这个格式。