专栏文章 一招轻松提高遍历 pocoUI 树的效率

fishfish-yu · 2020年01月15日 · 1595 次阅读

“以下文章来源于 “遍历 pocoUI 树的效率”,作者 saint_228。”

前言

在 Poco 的实际应用过程中,我们可能会经常遇到需要遍历 pocoUI 树的情况。整个项目的 UI 树,体积有可能非常庞大。

举个例子,将一个项目完整的 UI 树以字典的形式存储在 txt 文本里,容量也可能高达几百 K。

image

所以在需要遍历 UI 树或者进行复杂选择的情况下,我们需要用一些方法,来提高遍历 UI 树的效率。下文将介绍几种遍历 UI 树的方法以及其遍历效率。

(PS:可以使用如下代码抓取页面中的所有元素)

poco = poco(...)
ui = poco.agent.hierarchy.dump()

方法 1:offspring()

poco 树中的各种控件都有其属性,下面就以项目中“UICarmeraRoot”这个对象为例:

image

查询 UI 树里,最偷懒的方法就是直接生成一个全树的实例,然后取其所有后代,再在其后代中逐个查找符合条件的属性对应的对象。
示例代码为:

uioffspring=poco("UIRoot(Clone)").offspring()
for x in uioffspring:
    if x.attr("name") == 'UICameraRoot':
        print(x.get_text())
        print('查找控件过程结束')  
        break
    else :
        print("没找到")

从图中我们可以知道这段代码执行所需时间为:144 秒。

image

分析一下,瓶颈应该出在if x.attr("name") == 'UICameraRoot'这句:

image

每次对 poco 实例的属性进行操作,都需要重新生成一个dump,这非常耗费资源。
除非在之后的业务逻辑里要对offspring()进行不可同步的操作,否则不建议采取这种方法。

方法 2:offspring() 指定参数

offspring()方法里跟指定参数,可以将dump操作缩减为 1 次。大大提升效率。
示例代码如下:

ui_node_name = poco("UIRoot(Clone)").offspring(name='UICameraRoot')
print('查找控件XX结束')  

代码精简,效率也提升了几十倍。花费了 4.8 秒就从整个 UI 树中查询到了我们需要的控件。

image

方法 3:freeze()

poco 还给我们提供了freeze()方法,即冻结 UI,能再次加快查询效率。

image

freeze()的源码可以看到,freeze()得到的是当前 poco 实例的一个静态副本。
我们可以通过下面的方式取到freeze实例

poco = Poco(...)
frozen_poco = poco.freeze()
hierarchy_dict = frozen_poco.agent.hierarchy.dump()

尝试将这个freeze()实例应用到上面的方法 1 和方法 2 中,具体应用如下:

  • 将方法 1 里的uioffspring=poco("UIRoot(Clone)").offspring()替换为uioffspring=freeze("UIRoot(Clone)").offspring()
  • 将方法 2 里的ui_node_name=poco("UIRoot(Clone)").offspring(name='UICameraRoot')替换为ui_node_name=freeze("UIRoot(Clone)").offspring(name='UICameraRoot')

结果如下:

image

image

可以看出两种算法的耗时相差无几。且都低于对 poco 实例的操作耗时。

我们从原理来理解一下:冻结 UI 其实就是将当前界面的层次结构包括所有 UI 的属性信息抓取下来,然后存到内存里。在跟 UI 交互时,就直接从内存里读取 UI 属性,而不用再发送 rpc 请求到 game/app 里去操作 UI。

好处就是一次抓取 (消耗几百毫秒),可以使用多次,读取 UI 属性几乎不消耗时间,效率极高。

但是坏处在于,你需要手动处理 UI 同步;如果抓取了层次结构后,某个 UI 控件位置发生了变化,此时如果仍然点击这个 UI 的话,就会点击到原来的位置上,而不是最新的位置,这很容易导致奇怪的测试结果;并且使用完最好也尽快释放,否则会占据大量的内存资源。

小结

  1. 除非需要操作整个 UI 树实例里的不同对象,否则尽量不要读取全树
  2. 读全树的效率比读单个控件的效率低 2 个数量级。
  3. freeze()的效率极高。但需要手动刷新

大家需要根据自己项目的实际情况,来选取合适的遍历 UI 树的方法。

关注下方公众号,可以查看更多往期教程;回复 “交流群” 即可加入我们的测试开发官方交流群

暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册