STF STF 集成 iOS 之源码分析

琉丶言 · June 25, 2019 · Last by 蓝蓝 replied at July 05, 2019 · 3643 hits

目前,iOS 的设备云真机集群工具在国内外普遍处于收费模式,国内的 ios 远程真机技术基本都掌握在互联网巨头手里(如 BAT),个人感觉这个并不是一个好的现象,需要一些力量来打破现状。然而开源社区的一些 ios 远程真机也做的差强人意,其中如 ios-remote 采用 ios-minicap,致命的缺点是一台 mac 只能带一台手机,这么高的成本导致不适合做二次开发和推广。atxserver2 里面提供 ios-proivder 由于作者 codeskybule 工作太忙,做的不是特别精细,其 ios 云真机的流畅度和远程操作性还有提升空间。macaca 的 ios 功能没使用过,但是曾经用 macaca 和 uirecorder 封装过 web 平台针对 andriod app 的录制回放平台,感觉其代码水平应该是阿里的实习生写的,其功能并不好用(实话实说),其他开源 ios 云真机平台在这就不一一点评了,有点偏题了,下面进入正题。

技术背景:

由于 appium 1.9 以后在 WebDriverAgent 加入了一个 mjpegServer,通过私有 api 的方式获取屏幕截图(WDA 编译能不能通过跟 Xcode 有关,Xcode10.2 及以上私有 api 被删掉,所以请选择 Xcode10.1 及以下)。真机实测,在大屏幕手机上,帧率可达到 25 帧左右,而在小屏幕手机上,可达到 40fps 以上,且延时在 100ms 左右。这个帧率和延时肉眼基本看不出差异了。
综上,我们选择 appium 的 WebDriverAgent 来提供截图服务,WDA 默认监听 9100 端口,我们使用 iproxy 将手机的 9100 端口映射到本机的端口,这样手机和主机之间的屏幕传输走 usb,不会因为手机的网络导致延时和卡顿。@mrx102 虎牙哥之前的三篇帖子已经分析讲得很清楚了,而且也已经开源了,项目地址:https://github.com/mrx1203/stf.git https://github.com/mrx1203/WebDriverAgent.git
该项目是完全结合了开源框架 OpenSTF,非常容易上手,适合做二次开发和商业化的云真机平台,其 ios 云真机画面流畅且远程操作性已经算是国内开源 ios 云真机中最好的(实测比美国公司 HeadSpin 的内部最新 ios 云真机技术流畅度稍微差一点点),而且一台 mac 可以带多台 iphone 设备(1 拖 N),真正意义上的终结了国内外 ios 远程真机技术被封闭垄断的现象。如果大家有兴趣搭建部署的话,可以加入 STF 技术交流 QQ 群:168170256,可以找源码作者答疑。

STF 架构源码分析:

如果你有 node 项目部署经验应该能够很容易看懂下面的源码目录,源码目录详解:
/.tx # 集成翻译平台 Transifex 的相关配置,用于语言翻译
/bin # 启动文件,调用的是 lib/cli.js
/docker #Docker 的相关配置
/lib # 后端
/res # 前端
/doc # 文档说明,包含 VNC,部署,API
/test # 状态检测
/vendor # 安装到手机上的应用和服务,包括 minirev,minitouch,STFService


前端 res
主要为 Service 服务和 View 页面两大部分
/app # 前端 web

  • /components/stf # 服务组件,用于与后端通讯
  • /control-panes # 页面,设备控制
  • /device-list # 页面,设备列表
  • /docs # 页面,帮助
  • /layout # 页面,布局
  • /menu # 页面,菜单
  • /setting # 页面,设置
  • /terminal # 页面,终端样式
  • /user # 页面,用户,暂未完成
  • /views # 页面,主视图
    /auth # 登录认证
  • /ladp # 页面,LADP 认证
  • /mock # 页面,默认验证 /common # 其他
  • /lang # 语言包
  • /logo # 页面,图标
  • /status # 页面,脚本 /test # 登录,跳转 /web_modules # 样式

后端 lib
/cli # 引入后端功能模块的 index.js 文件
/db # 数据操作相关文件

/units # 核心代码,用于功能实现

  • /api # 主要的 RESTful APIs
  • /app # 提供主要 HTTP 服务,处理所有静态资源,包括图片,脚本和样式表
  • /auth # 授权验证,Mock auth,OAuth 2.0,LDAP,SAML 2.0
  • /device # 设备功能的具体实现.设备端底包,服务和 STFservice.apk 的安装
  • /log # 将设备事件 log 存储至数据库
  • /notify # 用于推送通知或到你的环境
  • /pooxy # 数据库代理相关
  • /processer # 设备和 app 之间的桥,几乎所有通讯都经过它
  • /provider # 设备提供和发收命令
  • /reaper # 接收心跳,处理设备异常断连
  • /storage # 截图,图像存储和调整,操作 apk
  • /triproxy # 用于接受和处理来自 app 和设备端的请求
  • /websocket # 用于客户端 js 和服务端(ZeroMQ,Protobuf )的通讯,所有 action 均由此发送 /util # 内部方法 /wire # 队列,路由,流相关方法

ios 云真机模块:

为了不影响 STF 原来的目录,和原来的架构保持一致,ios 云真机模块主要在后端 lib 的/cli 下创建新的 ios-device、ios-provider、local 文件夹和/units 下重新创建了与/cli 对应的功能模块 ios-device、ios-provider 文件夹,用于来实现 ios 设备接入 STF 前端(STF 前端无需修改)。
首先,/cli 下 local 文件夹(stf local 本地方式启动 STF 服务,用于调试),参考原来的 index.js 增加定义了 ios 云真机需要用到的 wda 的端口、wda 相关启动路径和 ios-provider


同理/cli 下 ios-device 引入 units/ios-device 的功能模块

同理/cli 下 ios-provider 引入 units/ios-provider 的功能模块

这里传入的参数一定要保持正确,否则,很容易导致 units/ios-provider 模块 options.fork 方法参数识别不正确,请注意的参数里面增加了 wda-port、wda-path、type 的字段,type 这个字段是用来描述 ios 设备连接状态:离线、已授权、未授权、连接中等。真机设备就绪后可以为 device 状态,模拟器设备可以为 emulator 状态,后面设备连接成功前端收到设备信息后会在前端对应显示空闲或者繁忙。
其次,对应的功能模块 units/ios-porvider,去掉了 minicap 和 adb 相关的内容,里面主要定义了 porvider 里面 idevice.js 文件发现的所有 ios 设备的存储 lists 和两个状态 ready[ ] 和 waiting[ ] 的设备列表,当 cli/ios-provider 模块 options.fork 和 ios 设备子进程加载成功后,收到设备进程的 ready 消息会被添加到 ready 列表,同时从 waiting 列表移除。接下来最关键就是下面这段代码

调用链如下:匿名函数发送 DeviceIntroductionMessage 消息返回 register-->register.then(()=>check())-->work-->spawn-->开启 device 子进程
最后,上面提到了设备连接成功后会向前端发送设备信息,在设备开始连接的时候,ios 设备子进程会加载很多依赖的模块,打开文件/stf/lib/units/ios-device/index.js

其中 solo.js 这个依赖文件 :有两个作用 1:通过 wire.uitl 传递 DeviceIdentityMessage 设备信息:、2:通知前端 DeviceReadyMessage 是否就绪的重要代码。
进一步解析,DeviceIdentityMessage 内封装的设备信息来源于哪里,可以发现 solo 文件中引用了 identity 模块,打开/stf/lib/units/ios-device/plugins/util/identity.js 文件
里面包括一个用于获取设备的基础信息 properties,一个用于获取设备的屏幕信息 display,一个包括设备的硬件网络等信息 phone
display 使用采用 libimobiledevice 工具集下 idevicescreenshot 获取
phone 和 properities 采用 libimobiledevice 工具集下的 ideviceinfo -u 获取
具体获取方式可以查看这两个对应的 display.js 和 phone.js 文件

ios 云真机模块之设备发现和屏幕传输:

下面主要讲解 ios 设备发现和 mjpeg 视频流传输及解析部分,对于熟悉 STF 的同学,肯定会觉得这两个模块才算是精华部分,下面着重讲解下这两个部分
云真机设备发现模块主要依靠:\lib\units\ios-device\plugins\idevice.js 这个文件,这个文件定义了发现真机设备和模拟设备的,包括增加设备、删除设备和更新设备。使用的工具还是采用 libimobiledevice 工具集下 idevice_id -l

然后,怎么获取设备的视频图像呢,打开\lib\units\ios-device\plugins\screen\stream.js,这个文件也是复用了 STF 之前的 stream.js 插件,不过去掉了 adb 和 minicap 相关的内容,这个文件的作用是把视频图像(appium wda Mjpeg server 默认 9100 端口出来的图像帧)经过解析(this.parser = new FrameParser())后以一定帧率通过 websocket 传到前端显示


对 wda 源码修改和数据调优相关功能分别可以看 wda OC 源码和@mrx102 虎牙哥的 STF 集成 iOS 之远程控制

ios 云真机模块之远程控制:

远程操作使用 WDA 来驱动,只是 WDA 中的点击/滑动是与控件关联的,而我们的使用场景无需关联控件,直接通过坐标来实现,重写或者增加了 WDA 的点击和滑动的接口。
打开文件 stf/lib/units/device/plugins/touch/index.js,可以看到对应的坐标、按键等处理模块

总结:

除了基本 ios 设备操作,还有屏幕旋转、ipa 上传,ios 设备调试日志功能、远程调试等相关的代码功能在这里就不一一分析了,有兴趣自行研究源码或者可以来群里讨论。
附上一张截图:
BUG:目前来说唯一发现一个小问题就是采用 sharp 工具来压缩 frame 图像帧后 toBuffer(), 会在后台报一个某些 frame 压缩后不是 buffer 而是 obeject 对象的警告错误,但是不影响正常使用,有强迫症的同学,这里可以使用 gm 工具代替,但是 gm 的压缩工具性能不如 sharp,所以还是最高使用 sharp。


应楼下同学要求,最后再放一个演示视频:

gif 效果不好,可以来 STF 技术交流群观看 mp4 演示效果。

参考资料:

https://testerhome.com/topics/19548
https://testerhome.com/topics/13680

共收到 11 条回复 时间 点赞

沙发,狂顶!

我第二个顶,点赞

赞!好文!

6666,不愧是大佬,很有深度

感谢分享技术干货,深度好文

请问支持的 ios 版本有限制吗?

@liuyan 能否放个 video 或者 gif 看下效果呢?

麦子 回复

可以来 QQ 群:168170256 里看,我上传了 mp4

琉丶言 #10 · June 25, 2019 Author
Bob 回复

ios10 以上效果好

👍 牛掰,看 MP4 好流畅,为咋我装完,用着很不流畅😭 😭

需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up