当我使用 STF 时,最震惊的是,它怎么做到设备和前端页面设备模块操作上的同步。之前看STF 框架之 minicap 工具, 知道作者开发自己的 Android 设备上快速截图的工具,但是 STF 怎么将截图以这么快的速度传输到前端页面的呢?很好奇,所以有了这篇文章。

基础准备

STF 依赖技术

阅读 STF 源码,除熟悉 javascript 基础语法,express 框架需要知道一些基本概念。若想要改造 STF 前端,angular 1.x 框架必须好好学一学。

Websocket 协议

STF 服务端和 STF 前端通信协议是 Websocket,不是 HTTP。Websocket 是浏览器端新的传输协议,类似于 socket。因为这个协议,STF 能快速将截图从服务端同步给前端。我们先了解这个协议。

实时显示设备截图功能源码分析

STF 实时显示设备截图流程

将这个过程分为 从设备实时传输图片二进制文件至前端,以及前端渲染图片两个部分。

实时传输设备图片二进制文件源码分析

STF 实时传输设备图片二进制文件是来自如下文件:

stream.js 做了两件事:

不关心 STF 强大的截图工具 minicap,只需要明白图片二进制文件如何从设备传输至前端。

1.简单的实时传输图片二进制文件到前端页面的 demo

STF 官方文档 minicap 的使用 demo,这个 demo 实现了这样一个功能:

安装 minicap 工具在手机上,执行命令adb forward tcp:1717 localabstract:minicap,此时将设备的 TCP 服务器端口映射到本机的 1717 端口。nodejs 启动代码中 app.js,发现手机上的截图不停显示在 localhost:9002 页面上。这个 demo 是 STF 中传输设备图片二进制文件到前端的基本雏形。分析 demo 中 app.js

var WebSocketServer = require('ws').Server
  , http = require('http')
  , express = require('express')
  , path = require('path')
  , net = require('net')
  , app = express()

var PORT = process.env.PORT || 9002

app.use(express.static(path.join(__dirname, '/public')))

var server = http.createServer(app)
var wss = new WebSocketServer({ server: server })

wss.on('connection', function(ws) {
  console.info('Got a client')

  var stream = net.connect({
    port: 1717
  })

  stream.on('error', function() {
    console.error('Be sure to run `adb forward tcp:1717 localabstract:minicap`')
    process.exit(1)
  })


  function tryRead() {
    ....
    ....

    ws.send(frameBody, {
              binary: true
            })
  }

  stream.on('readable', tryRead)

  ws.on('close', function() {
    console.info('Lost a client')
    stream.end()
  })

  server.listen(PORT)

上述代码主要分为以下几块

2.STF 中实时传输设备截图代码分析

STF 中 stream.js 实现实时传输设备图片二进制文件代码,基本原理和上面的 demo 是一样的。只不过因为 STF 管理多台设备,代码会有点差别。

前端渲染图片

前端接收到二进制文件,如何渲染图片呢?这部分逻辑主要在${STFhome}/res/app/components/stf/screen/screen-directive.js文件中

var ws = new WebSocket(device.display.url)
ws.binaryType = 'blob'

 ws.onmessage = (function() {

    return function messageListener(message) {
        if (message.data instanceof Blob) {
            var blob = new Blob([message.data], {
                  type: 'image/jpeg'
                })

            ...
            ...

            var img = imagePool.next()

            var url = URL.createObjectURL(blob)

          img.src = url
        }
    }
 })()

单独拎出来这段代码。这种更新前端图片的流程给我提供了新思路。

后记

STF 实时显示设备截图功能涉及的知识点很多:Android,tcp 通信,浏览器 Websocket 协议,blob 对象等。只觉得写这个工具的作者牛 X。


↙↙↙阅读原文可查看相关链接,并与作者交流