中文 | English
该 Demo 是端云交互的最简 Demo:端侧发消息到云侧,云侧收到后直接返回。消息通道用 USB + adb reverse 模拟,不经过公网。
没有真正的云端服务器,也没有固定公网 IP。用本地电脑模拟云端;用 adb reverse 把本机端口映射到手机的 127.0.0.1,手机像连本机一样连到电脑上的 Agent。
- 端侧:
client/(Android arm64-v8a 手机 App) - 云侧:
server/(本机 TCP 回显进程,经adb reverse对手机表现为127.0.0.1:17890)
云侧(本机 Agent) |
端侧(Client) |
对照实现:通道与报文来自 auto_procedure 的 ADB reverse + 行分隔 JSON;Demo 形态对齐 android-device-cloud-ngrok 的回显端云 Demo(该仓库走 ngrok HTTPS;本仓库改为 ADB)。
语音输入与 android-vad-asr-demo 相同,使用端侧 sherpa-onnx。model.int8.onnx 等模型未随仓库提供;不下载模型则不支持语音输入,界面只保留文字输入和发送。
| 序号 | 改什么 | 文件 / 变量 | 怎么改 |
|---|---|---|---|
| 1 | 语音模型(可选) | client/app/src/main/assets/sherpa-onnx-paraformer-zh-2023-09-14/model.int8.onnx、tokens.txt;JNI:client/app/src/main/jniLibs/arm64-v8a/ |
按 android-vad-asr-demo 下载放入。不放则无语音,只留文字输入和发送 |
默认端口是 17890。若要改端口,同时改 server/start.sh 的 PORT 和 client/.../AdbEndpoint.kt 的 DEFAULT_PORT。
端侧发出一句话,云侧原样返回,手机屏幕显示同一段文字。不跑产品 Agent,只验证通道是否通。
端侧(client)
- 文字输入后点「发送」。
- 若已放入语音模型:点麦克风,本地转写成文字再发送(不上传音频)。未放入模型时,只显示文字输入和发送。
- 点「连接」:App 连
127.0.0.1:17890,流量经adb reverse到达电脑。
云侧(server)
- 没有云主机:
echo_server.py跑在本机,监听127.0.0.1:17890,收到一行 JSON 后原样写回。 - 没有公网入口:
adb reverse tcp:17890 tcp:17890把手机上的127.0.0.1:17890转到电脑的同一端口。 - TCP 连上即上线,断开即下线。
- 手机必须用 USB 打开调试,并与电脑上的
adb可见。
端侧经 ADB reverse 把消息送到本机上的「云侧」,本机原样回写。没有真云、没有固定 IP,ADB 只负责把电脑端口扮成手机的 localhost。数据路径:
手机 App --TCP--> 127.0.0.1:17890
│
adb reverse tcp:17890 tcp:17890
▼
电脑 echo_server <--listen-- 127.0.0.1:17890
│
原样返回一行 JSON
▼
手机界面显示服务端返回的 text
消息是一行 UTF-8 JSON:
{"type":"chat","text":"你好"}语音只在手机本地转成这段 text,线上没有音频。手机连的是自己的 127.0.0.1;USB 上的 adb reverse 把这个端口转到电脑上的回显进程。
手机要和电脑上的服务对话,常见接法如下。本 Demo 选用 USB + adb reverse。
| 方式 | 做法 | 优势 | 劣势 |
|---|---|---|---|
USB + adb reverse(本 Demo) |
数据线连接,电脑把本机端口转到手机的 127.0.0.1 |
不暴露公网、调试简单、延迟低 | 必须插 USB、依赖 adb,不能给真实用户用 |
| 同一局域网 | 手机访问电脑的局域网 IP + 端口 | 不依赖第三方、实现直接 | 必须同一 Wi-Fi;IP 常变;防火墙/隔离 AP 会拦;一般没有现成 HTTPS |
| ngrok 隧道 | 本机端口映射到固定公网 HTTPS | 任意网络可连、有 HTTPS、不用 USB、不用同一 Wi-Fi | 依赖 ngrok 账号与额度;免费域名有拦截页 |
| 自建隧道(frp / Cloudflare Tunnel / Tailscale) | 自己搭或用其它穿透服务,效果类似 ngrok | 可控性更高,可走内网或自有域名 | 要额外部署或账号;接入成本比现成 ngrok 高 |
| 公网云端服务 | 把服务部署到云主机 / API 网关,手机直连 | 接近产品形态,可多用户、可扩展 | 不再是「电脑上的本地 Agent」;要部署、证书和运维 |
client/connect 把连接和报文分开:TcpServerConnection 只负责连上 127.0.0.1:17890;LineMessageClient 负责一行一条 JSON。以后换成 HTTP/TLS 时,可以另写一个 ServerConnection,不必改报文层。
语音识别在手机本地完成,方案与 android-vad-asr-demo 一致:sherpa-onnx + Paraformer 中文模型。未放入 model.int8.onnx 时,运行期关闭语音入口。
- 用 USB 连接 arm64 真机
- 打开开发者选项和 USB 调试
- 本机执行
adb devices,应看到device状态
chmod +x server/start.sh
./server/start.sh脚本会:
- 结束占用
17890的旧进程 - 执行
adb reverse tcp:17890 tcp:17890 - 在
17890拉起echo_server.py
若手机是在脚本启动之后才插上的,再执行一次:
adb reverse tcp:17890 tcp:17890只测本机回显(不经过手机):
python3 server/echo_server.py --port 17890
python3 - <<'PY'
import socket
s = socket.create_connection(("127.0.0.1", 17890), timeout=3)
s.sendall('{"type":"chat","text":"你好"}\n'.encode("utf-8"))
print(s.recv(4096).decode("utf-8"), end="")
s.close()
PY应打印 {"type":"chat","text":"你好"}。
用 Android Studio 打开 client/,连 arm64 真机 Run。
- 电脑端已运行
./server/start.sh - 打开 App(会自动连
127.0.0.1:17890);若未连上,点「连接」 - 输入文字,点「发送」
- 下方应出现服务端原样返回的文字
App 不填 URL:通道对手机来说就是本机 TCP。Manifest 已声明 INTERNET(本机 socket 需要),usesCleartextTraffic 保留以便以后换成明文 HTTP。
语音输入与 android-vad-asr-demo 相同。模型下载与 .so 放置见该仓库说明。
model.int8.onnx 约 232MB,不纳入本仓库。需要语音时,按该 Demo 把模型放到:
client/app/src/main/assets/sherpa-onnx-paraformer-zh-2023-09-14/model.int8.onnx
client/app/src/main/assets/sherpa-onnx-paraformer-zh-2023-09-14/tokens.txt
JNI 库放到 client/app/src/main/jniLibs/arm64-v8a/。
不下载模型则不支持语音输入:启动时若 assets 中没有 model.int8.onnx 或其它 .onnx,界面只展示文字输入和发送。
android-device-cloud-adb/
├── README.md # 中文说明
├── README.en.md # English
├── server/
│ ├── echo_server.py # 本地 TCP 回显
│ └── start.sh # adb reverse + 回显
└── client/ # Android 工程
├── app/ # 聊天 UI、可选端侧 ASR
└── connect/ # TcpServerConnection + LineMessageClient

