手把手教你搭建Android云真机平台

背景

 市面上的云真机不少,Testin云测, 腾讯的WeTest, 友盟+的U-APM, 泽众云-Alltesting,优测,岩鼠 ...,各个平台云真机租用的价格大多是 1 元 / 分钟,有点小贵, Testin 的最贵, 3 元 / 分钟,看着商业价值较大,引起了我的关注,决定研究一下核心原理。

核心原理

Android真机的远程控制主要还是基于 minicap+minitouch+atx-agent,实现界面展示与操作同步。

minicap的功能是把真机界面同步回传到PC端服务器,minitouch用于向真机派发触摸,滑屏,缩放等事件。

minitouch 也是通过 socket 通信进行操作,只不过 minicap 是读取 socket 信息,而 minitouch 则是向 socket 发送信息,进行设备操作,每一个触摸操作为一条 socket 命令。

atx-agent:可以捕捉PC端对回传图像的操作,调用minitouch, 触发真机执行对应的操作

minicap简介  

  minicap是一个可以远程获取android屏幕画面的开源库,它在低版本的Android系统上采用截屏的方式获取画面,在Android4.2以上系统上采用创建VirtualDisplay的方式来获取画面、性能大大提高。 minicap的核心功能都在minicap.so中实现,如果要进行二次开发直接引用即可。

1.mincap下载

1.1 clone代码

git clone https://github.com/openstf/minicap.git
cd minicap
git submodule init       // 初始化jni/vendor/libjpeg-turbo子仓库配置
git submodule update // 更新jni/vendor/libjpeg-turbo子仓库代码

1.2 minicap仓库目录结构

复制代码
minicap
  |-example  简单的示例客户端,连接minicap服务端,转换为web显示
  |-jni 源代码,minicap为纯c/c++
  |  |-minicap 简单的示例服务端,实现将捕获到的帧通过网络传输的功能
  |  |-minicap-shared 核心功能库,实现捕获画面、并通知listener的功能
  |  |  |-aosp 实际实现
  |  |    |-libs 已经编译好的so库
  |  |      |-android-xx 对应API版本为xx的so库,实现方法有所不同
  |  |    |-src 源代码
  |  |  |-mock 空实现
  |  |-vendor 依赖的第三方库
  |  |
  |-libs 编译后的输出目录
复制代码

2.mincap编译

2.1 下载ndk-build

顺道插一句,刚开始我下载的是Android-Studio,体积很大,下载下来还不能使用。

2.2 用ndk-build 编译mincap安装包

2.2.1 下载配置adb

adb(Android Debug Bridge),安卓平台调试桥,是连接Android手机与PC端的桥梁,通过adb可以管理、操作模拟器和设备,如安装软件、查看设备软硬件参数、系统升级、运行shell命令等。

1) 下载安装adb 

2) 在环境变量配置中添加adb路径,按键Windows+R打开运行,输入sysdm.cpl,回车,弹出系统属性对话框  在高级==》环境变量==》系统变量==》path,添加adb可执行文件的文件夹路径。

2.2.2 连接设备

需要手机打开调试者模式,打开  设置==>开发人员选项==>USB调试,打开即可。开发人员选项在Android4.2以下,可以直接看到。 在Android 4.2及以上版本中默认是隐藏的。打开开发人员选项的方法是:打开 设置==>关于手机,连续点击版本号7次即可。

首次调试,手机会弹出是否允许某台电脑以USB方式调试该手机的问询对话框,勾选允许使用这台计算机进行调试。

               

然后用adb devices命令,查看已连接的设备。如果设备列表为空,可以尝试一下:1)重新开启USB调试权限 ; 2)撤销USB调试权限重新赋权;3) 重启手机

adb devices

 2.2.3 查看手机CPU支持的ABI(CPU指令架构)

 不同的Android手机使用不同的CPU,而不同的 CPU支持不同的指令集,CPU与指令集的每种组合都有专属的应用二进制接口,即 ABI(全称:ApplicationBinary Interface)

adb shell getprop ro.product.cpu.abi

2.2.4  获取手机设备的sdk

adb shell getprop ro.build.version.sdk

2.2.5 执行编译

ndk-build可以通过APP_PLATFORM参数设置目标平台,默认是android-14,这里指定的是android-26(8.0),实际并没有什么影响,因为只有minicap-shared的实现是平台相关的,但我们使用了编译好的库;通过APP_ABI可以指定指令平台,如果不指定默认将编译全部的armeabi-v7a、arm64-v8a、x86、x86_64平台。

执行完毕后,在libs/armeabi-v7a下会生成minicap、minicap.so、minicap-nopie三个文件,实际用到的只有minicap,这里生成的minicap.so是一个空实现。

ndk-build.cmd APP_PLATFORM=android-26 APP_ABI=arm64-v8a

3. 运行minicap项目示例

3.1 运行minicap项目中的example内的服务端

3.1.1 将编译好的两个文件push到手机设备上

so文件一定用minicap-master\jni\minicap-shared\aosp\libs\android-26\arm64-v8a\minicap.so,cpu架构要匹配.否则会导致与系统不兼容,运行会报错

adb push  libs/arm64-v8a/minicap data/local/tmp
adb push  jni/minicap-shared/aosp/libs/android-26/arm64-v8a/minicap.so data/local/tmp

3.1.2 设置文件执行权限

adb shell chmod 777 data/local/tmp/minicap
adb shell chmod 777 data/local/tmp/minicap.so

3.1.3 获取android设备分辨率

adb shell wm size

3.1.4 启动服务端

# 测试服务器是否可用
adb shell LD_LIBRARY_PATH=data/local/tmp data/local/tmp/minicap -P 1080x1920@1080x1920/0 -t

# 启动服务器
adb shell LD_LIBRARY_PATH=data/local/tmp data/local/tmp/minicap -P 1080x1920@1080x1920/0 

3.1.5 使用adb工具进行端口映射,将服务端的端口映射到1717,客户端node读取服务器传送数据的端口是1717

adb forward tcp:1717 localabstract:minicap

3.2  运行minicap项目中的example内的客户端

3.2.1 安装依赖

yarn

3.2.2  启动客户端

node app.js

minitouch简介

minitouch 是 openstf 基于 ndk + android 开发的用于模拟人类点击行为的操作库。这个库以高稳定性、反应快著称,比起 adb 操作与 uiautomator 都要更灵敏,被广泛用于 android 设备的精细操作。然而,因为其使用与安装的方式都较为繁琐,且无法定位到元素,使得它在自动化的应用领域上远远比不上 uiautomator。但它能够真正模拟物理点击的效果(uiautomator 属于软件层面上的模拟),更加接近真实点击的效果。同类产品在模拟点击时不会有"小圆圈",而 minitouch 有,表现与真实人手点击一致。

使用方法:
首先我们需要找出你的设备所支持的ABI

ABI=$(adb shell getprop ro.product.cpu.abi | tr -d '\r')

注意:如果你有多台设备连接的情况下并且你没有设置$ANDROID_SERIAL的话,你需要去指定设备 -s <serial>

推送对应的文件到设备上。

adb push libs/$ABI/minitouch /data/local/tmp/

注意如果你的SDK<16的情况下,你需要使用minitouch-nopie

当然你还需要更改下minitouch的执行权限。

chmod 777 /data/local/tmp/minitouch

并且通过下面的命令来判断是否已经操作成功了。

adb shell /data/local/tmp/minitouch -h

下来我们可以直接通过

adb shell /data/local/tmp/minitouch

来执行,这个时候设备就开始监听了。这个时候除非出现错误的消息或者说程序退出,我们需要进行端口转发 通过如下命令:

adb forward tcp:1111 localabstract:minitouch

现在我们就需要去连接对应的端口,获取数据了。 提示文档里面给的是通过nc localhost 1111,但是我们肯定不是这样子的,我们需要自己去创建一个socket来进行连接。获取socket对应的数据。

minitouch使用方法

Websocket连接 $DEVICE_URL/minitouch , 一行行的按照JSON的格式写入

注: 坐标原点始终是手机正放时候的左上角,使用者需要自己处理旋转的变化

Touch Down
坐标(X: 50%, Y: 50%), index代表第几个手指, pressure 是可选的。

{"operation": "d", "index": 0, "xP": 0.5, "yP": 0.5, "pressure": 50}

Touch Commit

{"operation": "c"}

Touch Move

{"operation": "m", "index": 0, "xP": 0.5, "yP": 0.5, "pressure": 50}

Touch Up

{"operation": "u", "index": 0}

点击x:20%, y:20,滑动到x:40%, y:50%

{"operation": "d", "index": 0, "xP": 0.20, "yP": 0.20, "pressure": 50}
{"operation": "c"}
{"operation": "m", "index": 0, "xP": 0.40, "yP": 0.50, "pressure": 50}
{"operation": "c"}
{"operation": "u", "index": 0}
{"operation": "c"}

minitouch的可读性很差,所以需要应用第三方库,简化操作。

atx-agent工作原理

 

整个过程

  1. 在移动设备上安装atx-agent(守护进程), 随后atx-agent启动uiautomator2服务(默认7912端口)进行监听
  2. 在PC上编写测试脚本并执行(相当于发送HTTP请求到移动设备的server端)
  3. 移动设备通过WIFI或USB接收到PC上发来的HTTP请求,执行制定的操作

如果看每个组成部分功能的话,你发现也做不了什么有实用价值的事情,可是如果将它们整合成一个系统,就具备了单个要素所没有的功能,就变得有实用价值了。

ATXServer2系统简介

  Smartphone Test Farm(简称STF)是一个web应用程序,主要用于从指定的浏览器中远程调试智能手机、智能手表等,可远程调试超过160多台设备。STF可以便捷的管理移动测试设备,提高测试机的高效使用、便捷移动测试。

  ATXServer2其实是在STF基础上,使用Python3+NodeJS+rethinkdb搭建的一个设备管理平台,功能和STF差不多,但是可以实现IOS设备远程控制。Android的远程控制主要还是基于 minicap+minitouch+atx-agent;IOS的远程控制主要还是基于appium fork的WebDriverAgent,因为appium在WDA中增加了MJPEG Server,所以让远程投屏成为了可能。(不过只支持iOS 12.0以上)

atxserver2是一个移动设备管理平台,主要是Python3+NodeJS+RethinkDB开发,可以直接使用docker部署,也可以手动部署。使用的学习,建议使用docker部署,因为简单快捷,学习原理的时候,建议手动部署,因为可以掌握更多关键细节。

我们用庖丁解牛,化整为零的方法分析一下这个系统。

手动部署的话需要安装的软件:

  • Nodejs
  • Python3.x(建议3.6.x或者3.7.x,不能高于3.9.9)
  • ADB,点击下载
  • RethinkDB

整体项目主要包括四部分:
1.RethinkDB : 这是一个开源的轻量级的数据库,是用来存储数据的, 官网:https://rethinkdb.com/
2.atxserver2 : 这一部分主要负责处理数据,显示与用户的前端交互等等,所以单独运行atxserver2也可以看到效果,当运行起来以候访问: IP:4000 就可以看到效果;
3.atxserver2-android-provider: 这一部分是接入安卓设备必须启动的项目,主要负责安卓设备和平台的交互工作, 要想接入安卓设备必须启动此项目;
4.atxserver2-ios-provider: 这一部分是接入IOS设备必须启动的项目,主要负责IOS设备和平台的交互工作, 同样的要想接入IOS设备必须启动此项目(注意需要MAC电脑)

atxserver2服务端项目结构

 atxserver2项目下载

git clone https://github.com/openatx/atxserver2

atxserver2服务端项目主要采用 Tornado框架与Element-UI,其中貌似还有点BootStrap;总之此项目负责前端显示的内容;

项目结构:

复制代码
|-- examples 存放着一个自动化的Demo,采用的自动化框架是uiautomator2,设备占用接口参考了openstf的API;
|-- scripts 主要放着一些用于调试开发的工具类等,比如想调试安卓项目可以单独运行fake_android_provider.py 与atxserver2-android-provider,并且其中的参数要改一些,一般不用太关注这个文件夹.
|-- static 静态目录 |-- templates 前端界面 |-- web 网页代码 |-- urls.py 路由整合文件 |-- settings.py 配置文件 |-- database.py 数据库操作相关 |-- utils.py 常用配置 |-- views 每个界面的逻辑 |-- slave.py 与atxslave通信用 |-- device.py 设备相关路由 |-- base.py 基于RequestHandler的基类
复制代码

所有的接口采用token认证,每个请求在 Header中增加 Authorization: Bearer xxxxx-token-xxxx,这个token值可以在个人的用户信息界面获取到,也存储在rethinkdb里;

接口都是通过web请求调用,可以用 “httpie” 这个工具或者直接用python的 request 方法调用;具体的API文档:https://github.com/openatx/atxserver2/blob/master/API.md

atxserver2安装部署

安装依赖,到根目录执行

pip3 install -r requirements.txt

启动atxserver服务,设置监听端口,进入atx2所在目录并执行

# 默认监听的就是这个地址
python main.py --port 4000  

atxserver2-android-provider简介

atxserver2-android-provider 用于和安卓设备进行数据交互;使用uiautomator2框架, 在屏幕的显示传输与交互上使用minicap与minitouch;工作流程是 atxserver2-android-provider 可以通过adb track-devices 自动发现已经接入的设备,当手机接入到电脑上时,会自动给手机安装minicap, minitouch, atx-agent, app-uiautomator-[test].apk, whatsinput-apk(接入的设备需要配置好开发者选项,打开允许ADB的一系列操作,可能有的还要输入密码)。

atxserver2-android-provider项目下载:

git clone https://github.com/openatx/atxserver2-android-provider.git

目录结构

复制代码
atxserver2-android-provider|-vendor 依赖的第三方库
  |  |-WhatsInput-1.0.apk  在PC端向真机输入内容的输入法
  |  |-atx-agent-0.10.0.zip  
  |  |-stf-binaries-0.3.0.zip   
  |  |-app-uiautomator-2.3.3    UI自动化测试工具
  |  |  |-app-uiautomator-test.apk
  |  |  |-app-uiautomator.apk
  |  |
  |-asyncadb.py: 主要是用来建立一个TCP连接,为以后如果要执行ADB命令做基础准备还有一些工具方法;
  |-device.py: 主要和安卓设备进行数据交互的类
  |-device_names.py: 设备名字字典
  |-main.py: atxserver2-android-provider入口,其中apk传输,设备清理,以及一些基类都在这里
  |-settings.py: atx_agent版本的存储位置
  |-heartbeat.py: 心跳协议,数据连接和判断设备是否连接的时候会用到 通过该协议,服务端(atxserver2)能够知道有哪些设备接入了系统。以及当前连接的设备的状态。协议基于WebSocket,传递的内容均为JSON格式.
复制代码

启动参数

命令行参数
--port         本地监听的端口号
--server       atxserver2的地址,默认localhost:4000
--allow-remote 允许远程设备,默认会忽略类似10.0.0.1:5555的设备
--owner        邮箱地址或用户所在Group名,如果设置了,默认连接的设备都为私有设备,只有owner或管理员账号能看到

wifi连接要加入 --allow-remote,连接方式:

1)首选确保服务器和安卓终端处于同一个网段。可以相互之间 ping2)使用 USB 接入一次 ATX2
3)adb -s 设备号 tcpip 端口号
4)断开 usb 连接,然后 adb connect  手机 ip : 端口号
5)最重要的一点就是 在 atx2 服务器启动的时候加上 : --allow-remote

atxserver2-android-provider提供的接口

主要有两个接口,冷却设备和安装应用。 要先认证,通过在url query中增加secret=来实现认证。secret可以在provider启动的时候看到,也可以去数据库里找;

安装应用

通过URL安装应用

http POST $SERVER/app/install?udid=${UDID} secret=$SECRET url=http://example.com/demo.apk

{
  "success": true,
  "output": "Success\r\n"
}

冷却设备

留出时间让设备降降温,以及做一些软件清理的工作

$ http POST $SERVER/cold?udid=${UDID}

{
  "success": true,
  "description": "Device is colding"
}

心跳协议

atxserver2和atxserver2-android-provider之间采用websocket协议,当前atxserver2的websocket地址为 : ws://$SERVER_HOST/websocket/heartbeat

握手请求:provider -> atxserver2

{
    "command": "handshake",
    "name": "--provider-name--",
    "owner": "--owner-of-devices--",
    "secret": "--需要跟server端保持一致,不会会被拒--",
    "url": "--provider-url--",
    "priority": 1
}

priority主要通过手工去设置,数值越大代表该provider性能越好,网速越快。 url字段代表provider的url,通过约定好的格式,可以实现安装,释放设备的操作

握手回复:atxserver2 -> provider

握手成功返回

{
  "success": true,
  "id": "xxxxx-xxxx-xxx"
}

失败返回

{
  "success": false,
  "description": "xxxx"
}

更新设备状态: provider -> atxserver2

Android设备上线

properties通常为不常变动的信息。

复制代码
{
  "command": "update",
  "platform": "android",
  "udid": "xxxx-设备的唯一编号-xxxxx",
  "properties": {
    "serial": "xxxx",
    "brand": "xxxx",
    "version": "xxxx",
    "model": "xxxx",
    "name": "xxxx"
  },
  "provider": {
    "atxAgentAddress": "10.0.0.1:7912",
    "remoteConnectAddress": "10.0.0.1:5555",
    "whatsInputAddress": "10.0.0.2:9955"
  }
}
复制代码

Android设备离线

当WebSocket断线时,这个时候需要重连,Provider需要重发一次手机的信息。

{
  "command": "update",
  "udid": "xxxx-设备的唯一编号-xxxxx",
  "provider": null
}

atxserver2-android-provider安装部署

安装依赖,到atxserver2-android-provider根目录执行

pip3 install -r requirements.txt

检查移动设备连接

adb devices -l

启动Provider服务连接宿主机,进入到atxserver2-android-provider根目录执行

python main.py --server 宿主机IP地址:4000 

Provider可以通过adb track-devices自动发现已经接入的设备,当手机接入到电脑上时,会自动给手机安装minicap,minitouch,atx-agent,app-uiautomator-[test].apk,whatsinput-apk。

IOS设备接入

IOS设备的接入,需要使用atxserver2-ios-provider,同时需要安装libimobiledevice和配置WebDriverAgent,还涉及到开发者证书,本文主要研究Android平台的设备管理,感兴趣的可以克隆IOS设备接入项目,参考这篇文章实践一下。

git clone https://github.com/openatx/atxserver2-ios-provider

RethinkDB数据库下载

todo--写入数据如何查看。

RethinkDB 是第一个为实时应用程序构建的开源可伸缩数据库。它公开了一种新的数据库访问模型 - 开发人员可以告诉数据库不断地将更新的查询结果实时推送到应用程序,而不是轮询更改。RethinkDB 允许开发人员在很短的时间内以更少的工作量构建可扩展的实时应用程序。

  • 用于构建实时 Web 应用程序的开源数据库
  • 存储无模式 JSON 文档的 NoSQL 数据库
  • 分布式数据库,易于扩展
  • 具有自动故障转移和强大容错功能的高可用性数据库

RethinkDB 官方 没有提供的Windows版的RethinkDB安装包,最后在别处找了一个Win版的RethinkDB-v2.4.1安装包,点击下载

Python下载

Windows平台 python 3.10.0 和rethinkdb v2.4.1有兼容性问题,atx-server2无法正常启动,报module collections has no attribute "Callable",很明显是版本问题引起的。要将python降级到3.9.9, 点击下载 ,选择自定义安装,后面方便设置python的环境变量

 要将python可执行文件路径添加到系统环境变量的path中,否则只能在cmd窗口下使用,不能在git bash命令窗口下用。

 

远程真机系统启动步骤

 step1 atxserver2依赖rethinkdb, 先要启动rethinkdb

rethinkdb --bind all --cache-size 8192 --http-port 28015

 step2 启动atxserver2,默认监听的是4000端口

python main.py --port 4000

 step3 启动 atxserver2-android-provider 客户端,Android设备接入   --server 指定连接的远程主机,示例中是默认的端口

python main.py --server localhost:4000

问题最多的就是这一步,遇到过两个报错,

第一个报错是WhatsInput.apk安装失败,解决方法是替换项目下的requirements.txt中adbutils的版本,修改成0.9.4

logzero==1.5.*
adbutils>=0.8.1,<1.0 
tornado
requests
apkutils2
uiautomator2>=2.5.9
humanize

第二个报错是报设备找不到

 经查是 forward atx-agent 初次执行失败引起的,然后程序就中断了,简单粗暴的解决方式就是直接return

 

如果在ATX软件上意外关闭了atx-agent服务, 手动启动的方式是

adb shell
chmod 755 /data/local/tmp/atx-agent 
# 启动atx-agent并切换到后台运行
/data/local/tmp/atx-agent server -d 

step4 接入手机设备(前提是已经开启了USB调试模式),将充电模式切换成文件传输模式

 step5 访问localhost:4000,就能看到设备列表

注意:华为手机 键盘设置 关闭安全输入法 否则会导致屏幕显示黑屏

 

 参考文档

 

 

 

 

 

posted @   孤舟蓑翁  阅读(6339)  评论(1编辑  收藏  举报
编辑推荐:
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示