• Android之高效率截图


    本文来自网易云社区

    作者:孙圣翔


    在一张Android手机上截图有好多办法,为了能够高效率的截图,我几乎把所有的方法都尝试了一般。走了好多路,也遇到了好多的问题。

    只是想记录下这其中的不容易。

    下面所有的测试都是用的我的三星 S4.

    屏幕分辨率 1080x1920

    androidviewclient

    截图速度: 4.5s

    最开始截图用的是 google官方提供的纯python库androidviewclient,代码的地址在 https://github.com/dtmilano/AndroidViewClient

    基于adb协议,只能在电脑上用。最初被我用在的一个手游自动化测试工具airtest上面。使用它很简单,我写个简单的例子

    from com.dtmilano.android.viewclient import ViewClient
    c, _ = ViewClient.connectToDeviceOrExit(verbose=False, serialno='10.242.74.241:5555')
    s = c.takeSnapshot()
    s.save('snapshot.png', 'PNG')

    不过这个python库也有坑人的地方。它更新到pypi的时候,所有的历史版本都找不到。使得可以更新过去,但是更新不回来。

    最新的版本在有些机器上还跑不了。截图的时候某些手机还会出现图片缺少颜色的问题。


    screencap

    截图速度: 2.0s

    Android手机上自带有一个截图工具,一般都是被放在了/system/bin/screencap下。

    使用的时候需要在电脑上安装adb,然后adb shell进入shell环境,使用的时候,需要生成一个临时图片在手机上,然后把照片从手机上传输回来。

    可以写成一个批处理脚本

       @echo off
         adb shell screencap -p /sdcard/snapshot.png
         adb pull /sdcard/snapshot.png
         adb shell rm /sdcard/snapshot.png

    这个样子截图,要比androidviewclient的稳定性好很多。只是需要生成一个临时文件,感觉好别扭。

    APK程序直接截图

    截图速度: < 1s

    stackoverflow上也有不少代码例子。apk必须用java写,意味着我必须学一下java了,买了一本书《Android第一行代码》。

    学习了2个多星期,总算入门了。然后写了一个手机app截图。截图代码我就不贴了,这个比较长一点,网上也有很多例子。

    这种方法截图效率在1s以内。不过只有在当前App在前台运行的时候才可以截图。就算写成Service也不行。

    后来想想,截取不到图也算合理。假如一个App可以截取到其他App运行时的图片,岂不是越权了,用户的隐私还怎么保证。

    既然这样,只能放弃了。

    ASL

    之后有幸看到了google出的一个android-screenshot-library的东西,简称ASL。代码在http://code.google.com/p/android-screenshot-library/

    看到这个东西真是让人欣喜若狂。立马下载下来试了试,心情立马就不好了,截图来的图,竟然是黑屏。接着又借了4个手机试验。

    结果截图只有一台手机截出来的图能看(还是缺少一个颜色通道的那种)。 看看了代码实现的原理,是直接读取framebuffer。

    这个地方我解释下:

    在linux中,所有的东西通通都可以映射成文件,连屏幕映射成了文件。android的在/dev/graphics/fb0。

    通过读取fb0中的数据,然后在根据一些算法就可以还原出屏幕的图像了。

    还有一个库, http://code.google.com/p/android-fb2png/ 看代码原理应该和ASL差不多,不过实现了PC端的一个adb_screenshot的程序。

    没法截图怎么用啊,放弃吧。 哎

    重回screencap, Golang重写截图程序。

    截图速度: 1s

    好在android是开源的,直接可以翻到screencap实现的源码。意外的发现他有两种输出格式。

    一种是png格式 (耗时1.5s)

    还有一种是原始的图片格式(这种原始的格式,跟bmp差不多)。 试验了下,好使400ms

    之前看过一个韩国人写的remotedroid <https://code.google.com/p/remoteroid/> 截图速度快的让人震惊。

    所以我在想是不是screencap中png的压缩算法有问题。参考下代码中,他输出的格式。用Go语言写了一个转化的程序。

    // TakeSnapshot by cmd: /system/bin/screencap
    func TakeSnapshot() (img *image.RGBA, err error) {
        scrbf = bytes.NewBuffer(nil)
        cmd := exec.Command("screencap")
        cmd.Stdout = scrbf
        if err = cmd.Run(); err != nil {
            return
        }  
        var width, height, format int32
        binary.Read(scrbf, binary.LittleEndian, &width)
        binary.Read(scrbf, binary.LittleEndian, &height)
        err = binary.Read(scrbf, binary.LittleEndian, &format)
        if err != nil {
            return
        }  
        img = image.NewRGBA(image.Rectangle{image.ZP, image.Point{int(width), int(height)}})
        return
    }
    func main(){
         s, _ := TakeSnapshot()
         out, _ := os.Create("snapshot.png")
         defer out.Close()
         png.Encode(out, s)
    }


    利用总共用时1.2s的样子。比之前用screencap 2s快了不少哎。感觉似乎还可以更快点。把png改成jpeg试试。

    func main(){
         s, _ := TakeSnapshot()
         out, _ := os.Create("snapshot.png")
         defer out.Close()
         jpeg.Encode(out, s, jpeg.Options{60})
    }


    这种方法变成了1.1s, 感觉似乎还可以更快点。 需要稍微复杂点,需要减少内存申请和拷贝的次数。

    // TakeSnapshot by cmd: /system/bin/screencap
    
    var SCRBUFLEN int
    func TakeSnapshot() (img *image.RGBA, err error) {
        var scrbf *bytes.Buffer
        if SCRBUFLEN == 0 {
            scrbf = bytes.NewBuffer(nil)
        } else {
            scrbf = bytes.NewBuffer(make([]byte, 0, SCRBUFLEN))
        }  
        cmd := exec.Command("screencap")
        cmd.Stdout = scrbf
        if err = cmd.Run(); err != nil {
            return
        }  
        var width, height, format int32
        binary.Read(scrbf, binary.LittleEndian, &width)
        binary.Read(scrbf, binary.LittleEndian, &height)
        SCRBUFLEN = int(width * height * 4
        err = binary.Read(scrbf, binary.LittleEndian, &format)
        if err != nil {
            return
        }  
        w, h := int(width), int(height)
        img = &image.RGBA{scrbf.Bytes(), 4 * w, image.Rect(0, 0, w, h)}
        return
    }

    改完后,变成1.0s了。 终于到了还算可以接受的程度。 整理下代码终于可以让他抛头露面了。

    https://github.com/netease/airinput

    这就是我在做Android截图的时候,所遇到的大部分问题。要知道截个图是多么的不容易。 另外想说,请一定不要放弃,总会有办法的。


    网易云免费体验馆,0成本体验20+款云产品! 


    更多网易研发、产品、运营经验分享请访问网易云社区


    相关文章:
    【推荐】 从疑似华住集团4.93亿开房信息泄露看个人如何预防信息泄露
    【推荐】 Docker容器的原理与实践(上)

  • 相关阅读:
    说说渐进式增强
    Websocket模板
    Echart图表显示单位
    JS配置文件设置,共享变量抽取
    PHP处理字符串的10个简单方法
    C#实现只许一个实例运行(使用mutex类)
    实现PHP基本安全11条
    PHP开发不能违背的安全规则
    Querying a motor position
    log info
  • 原文地址:https://www.cnblogs.com/163yun/p/9707644.html
Copyright © 2020-2023  润新知