uiautomatorviewer 二次开发之自动生成控件定位符

uiautomatorviewer二次开发之自动生成控件定位符

前言
我们在使用Appium进行移动自动化测试脚本编写的时候,经常出现控件无法定位,如ListView下面的item,控件基本属性一样的、某些控件没有id、name等,这个时候,如果单纯靠id、name、text可能无法完全唯一定位一个控件,这个时候就需要编写xpath了,可是xpath语法、写法对于没有接触过的测试人员来说,又是个门槛,接下来,我们就来讨论如何通过二次开发uiautomatorviewer自动生成xpath供用户直接copy使用。
原理
uiautomatorviewer是android SDK包中原生的开源工具,给用户提供一种查看当前终端布局、控件属性的一个辅佐工具,
该工具的GUI是使用RCP组件进行开发的,然后通过uiautomator dump把当前终端布局文件dump到本地,uiautomatorviewer
通过xml布局文件,构造一棵tree,放到Canvas SWT组件中,和当前png截图叠加在一起,同时监听鼠标move等事件,自动
获取该tree的node节点,并且把该node节点的所有属性获取显示出来。


代码结构

com.android.uiautomator:存放uiautomatorviewer工具的GUI界面代码,其中主入口UiAutomatorViewer.java文件里面有main函数入口,工具的窗口就在此创建。
com.android.uiautomator.actions:存放所有anction操作,如:Device screenshot 、open等。
com.android.uiautomator.tree:存放tree封装,dump出来的xml解析成一棵完整的tree,这个包是核心包。

二次开发
首先,dump出来的xml文件被uiautomationviewer解析成自定义的tree,每个节点代表一个控件,所以,如何添加Xpath属性呢?只需要在node节点中添加一个字段即可,其实很简单。通过阅读代码,在com.android.uiautomator.tree包下,有个node节点封装类,UiNode.java,看下以下代码片源:

<span class="kd" style="color: rgb(170, 34, 255); font-weight: bold;">public</span> <span class="kd" style="color: rgb(170, 34, 255); font-weight: bold;">class</span> <span class="nc" style="color: blue;">UiNode</span> <span class="kd" style="color: rgb(170, 34, 255); font-weight: bold;">extends</span> <span class="n">BasicTreeNode</span> <span class="o" style="color: rgb(102, 102, 102);">{</span>
    <span class="kd" style="color: rgb(170, 34, 255); font-weight: bold;">private</span> <span class="kd" style="color: rgb(170, 34, 255); font-weight: bold;">static</span> <span class="kd" style="color: rgb(170, 34, 255); font-weight: bold;">final</span> <span class="n">Pattern</span> <span class="n">BOUNDS_PATTERN</span> <span class="o" style="color: rgb(102, 102, 102);">=</span> <span class="n">Pattern</span>
            <span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">compile</span><span class="o" style="color: rgb(102, 102, 102);">(</span><span class="s" style="color: rgb(187, 68, 68);">"\\[-?(\\d+),-?(\\d+)\\]\\[-?(\\d+),-?(\\d+)\\]"</span><span class="o" style="color: rgb(102, 102, 102);">);</span>

    <span class="kd" style="color: rgb(170, 34, 255); font-weight: bold;">private</span> <span class="kd" style="color: rgb(170, 34, 255); font-weight: bold;">final</span> <span class="n">Map</span><span class="o" style="color: rgb(102, 102, 102);"><</span><span class="n">String</span><span class="o" style="color: rgb(102, 102, 102);">,</span> <span class="n">String</span><span class="o" style="color: rgb(102, 102, 102);">></span> <span class="n">mAttributes</span> <span class="o" style="color: rgb(102, 102, 102);">=</span> <span class="k" style="color: rgb(170, 34, 255); font-weight: bold;">new</span> <span class="n">LinkedHashMap</span><span class="o" style="color: rgb(102, 102, 102);">();</span>
    <span class="kd" style="color: rgb(170, 34, 255); font-weight: bold;">private</span> <span class="n">String</span> <span class="n">mDisplayName</span> <span class="o" style="color: rgb(102, 102, 102);">=</span> <span class="s" style="color: rgb(187, 68, 68);">"ShouldNotSeeMe"</span><span class="o" style="color: rgb(102, 102, 102);">;</span>
    <span class="kd" style="color: rgb(170, 34, 255); font-weight: bold;">private</span> <span class="n">Object</span><span class="o" style="color: rgb(102, 102, 102);">[]</span> <span class="n">mCachedAttributesArray</span><span class="o" style="color: rgb(102, 102, 102);">;</span>

    <span class="kd" style="color: rgb(170, 34, 255); font-weight: bold;">public</span> <span class="kt" style="color: rgb(0, 187, 0); font-weight: bold;">void</span> <span class="n">addAtrribute</span><span class="o" style="color: rgb(102, 102, 102);">(</span><span class="n">String</span> <span class="n">key</span><span class="o" style="color: rgb(102, 102, 102);">,</span> <span class="n">String</span> <span class="n">value</span><span class="o" style="color: rgb(102, 102, 102);">)</span> <span class="o" style="color: rgb(102, 102, 102);">{</span>
        <span class="k" style="color: rgb(170, 34, 255); font-weight: bold;">this</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">mAttributes</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">put</span><span class="o" style="color: rgb(102, 102, 102);">(</span><span class="n">key</span><span class="o" style="color: rgb(102, 102, 102);">,</span> <span class="n">value</span><span class="o" style="color: rgb(102, 102, 102);">);</span>
        <span class="n">updateDisplayName</span><span class="o" style="color: rgb(102, 102, 102);">();</span>
        <span class="k" style="color: rgb(170, 34, 255); font-weight: bold;">if</span> <span class="o" style="color: rgb(102, 102, 102);">(</span><span class="s" style="color: rgb(187, 68, 68);">"bounds"</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">equals</span><span class="o" style="color: rgb(102, 102, 102);">(</span><span class="n">key</span><span class="o" style="color: rgb(102, 102, 102);">))</span>
            <span class="n">updateBounds</span><span class="o" style="color: rgb(102, 102, 102);">(</span><span class="n">value</span><span class="o" style="color: rgb(102, 102, 102);">);</span>
    <span class="o" style="color: rgb(102, 102, 102);">}</span>

    <span class="kd" style="color: rgb(170, 34, 255); font-weight: bold;">public</span> <span class="n">Map</span><span class="o" style="color: rgb(102, 102, 102);"><</span><span class="n">String</span><span class="o" style="color: rgb(102, 102, 102);">,</span> <span class="n">String</span><span class="o" style="color: rgb(102, 102, 102);">></span> <span class="n">getAttributes</span><span class="o" style="color: rgb(102, 102, 102);">()</span> <span class="o" style="color: rgb(102, 102, 102);">{</span>
        <span class="k" style="color: rgb(170, 34, 255); font-weight: bold;">return</span> <span class="n">Collections</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">unmodifiableMap</span><span class="o" style="color: rgb(102, 102, 102);">(</span><span class="k" style="color: rgb(170, 34, 255); font-weight: bold;">this</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">mAttributes</span><span class="o" style="color: rgb(102, 102, 102);">);</span>
    <span class="o" style="color: rgb(102, 102, 102);">}</span>

    <span class="kd" style="color: rgb(170, 34, 255); font-weight: bold;">private</span> <span class="kt" style="color: rgb(0, 187, 0); font-weight: bold;">void</span> <span class="n">updateDisplayName</span><span class="o" style="color: rgb(102, 102, 102);">()</span> <span class="o" style="color: rgb(102, 102, 102);">{</span>
        <span class="n">String</span> <span class="n">className</span> <span class="o" style="color: rgb(102, 102, 102);">=</span> <span class="o" style="color: rgb(102, 102, 102);">(</span><span class="n">String</span><span class="o" style="color: rgb(102, 102, 102);">)</span> <span class="k" style="color: rgb(170, 34, 255); font-weight: bold;">this</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">mAttributes</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">get</span><span class="o" style="color: rgb(102, 102, 102);">(</span><span class="s" style="color: rgb(187, 68, 68);">"class"</span><span class="o" style="color: rgb(102, 102, 102);">);</span>
        <span class="k" style="color: rgb(170, 34, 255); font-weight: bold;">if</span> <span class="o" style="color: rgb(102, 102, 102);">(</span><span class="n">className</span> <span class="o" style="color: rgb(102, 102, 102);">==</span> <span class="kc" style="color: rgb(170, 34, 255); font-weight: bold;">null</span><span class="o" style="color: rgb(102, 102, 102);">)</span>
            <span class="k" style="color: rgb(170, 34, 255); font-weight: bold;">return</span><span class="o" style="color: rgb(102, 102, 102);">;</span>
        <span class="n">String</span> <span class="n">text</span> <span class="o" style="color: rgb(102, 102, 102);">=</span> <span class="o" style="color: rgb(102, 102, 102);">(</span><span class="n">String</span><span class="o" style="color: rgb(102, 102, 102);">)</span> <span class="k" style="color: rgb(170, 34, 255); font-weight: bold;">this</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">mAttributes</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">get</span><span class="o" style="color: rgb(102, 102, 102);">(</span><span class="s" style="color: rgb(187, 68, 68);">"text"</span><span class="o" style="color: rgb(102, 102, 102);">);</span>
        <span class="k" style="color: rgb(170, 34, 255); font-weight: bold;">if</span> <span class="o" style="color: rgb(102, 102, 102);">(</span><span class="n">text</span> <span class="o" style="color: rgb(102, 102, 102);">==</span> <span class="kc" style="color: rgb(170, 34, 255); font-weight: bold;">null</span><span class="o" style="color: rgb(102, 102, 102);">)</span>
            <span class="k" style="color: rgb(170, 34, 255); font-weight: bold;">return</span><span class="o" style="color: rgb(102, 102, 102);">;</span>
        <span class="n">String</span> <span class="n">contentDescription</span> <span class="o" style="color: rgb(102, 102, 102);">=</span> <span class="o" style="color: rgb(102, 102, 102);">(</span><span class="n">String</span><span class="o" style="color: rgb(102, 102, 102);">)</span> <span class="k" style="color: rgb(170, 34, 255); font-weight: bold;">this</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">mAttributes</span>
                <span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">get</span><span class="o" style="color: rgb(102, 102, 102);">(</span><span class="s" style="color: rgb(187, 68, 68);">"content-desc"</span><span class="o" style="color: rgb(102, 102, 102);">);</span>
        <span class="k" style="color: rgb(170, 34, 255); font-weight: bold;">if</span> <span class="o" style="color: rgb(102, 102, 102);">(</span><span class="n">contentDescription</span> <span class="o" style="color: rgb(102, 102, 102);">==</span> <span class="kc" style="color: rgb(170, 34, 255); font-weight: bold;">null</span><span class="o" style="color: rgb(102, 102, 102);">)</span>
            <span class="k" style="color: rgb(170, 34, 255); font-weight: bold;">return</span><span class="o" style="color: rgb(102, 102, 102);">;</span>
        <span class="n">String</span> <span class="n">index</span> <span class="o" style="color: rgb(102, 102, 102);">=</span> <span class="o" style="color: rgb(102, 102, 102);">(</span><span class="n">String</span><span class="o" style="color: rgb(102, 102, 102);">)</span> <span class="k" style="color: rgb(170, 34, 255); font-weight: bold;">this</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">mAttributes</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">get</span><span class="o" style="color: rgb(102, 102, 102);">(</span><span class="s" style="color: rgb(187, 68, 68);">"index"</span><span class="o" style="color: rgb(102, 102, 102);">);</span>
        <span class="k" style="color: rgb(170, 34, 255); font-weight: bold;">if</span> <span class="o" style="color: rgb(102, 102, 102);">(</span><span class="n">index</span> <span class="o" style="color: rgb(102, 102, 102);">==</span> <span class="kc" style="color: rgb(170, 34, 255); font-weight: bold;">null</span><span class="o" style="color: rgb(102, 102, 102);">)</span>
            <span class="k" style="color: rgb(170, 34, 255); font-weight: bold;">return</span><span class="o" style="color: rgb(102, 102, 102);">;</span>
        <span class="n">String</span> <span class="n">bounds</span> <span class="o" style="color: rgb(102, 102, 102);">=</span> <span class="o" style="color: rgb(102, 102, 102);">(</span><span class="n">String</span><span class="o" style="color: rgb(102, 102, 102);">)</span> <span class="k" style="color: rgb(170, 34, 255); font-weight: bold;">this</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">mAttributes</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">get</span><span class="o" style="color: rgb(102, 102, 102);">(</span><span class="s" style="color: rgb(187, 68, 68);">"bounds"</span><span class="o" style="color: rgb(102, 102, 102);">);</span>
        <span class="k" style="color: rgb(170, 34, 255); font-weight: bold;">if</span> <span class="o" style="color: rgb(102, 102, 102);">(</span><span class="n">bounds</span> <span class="o" style="color: rgb(102, 102, 102);">==</span> <span class="kc" style="color: rgb(170, 34, 255); font-weight: bold;">null</span><span class="o" style="color: rgb(102, 102, 102);">)</span> <span class="o" style="color: rgb(102, 102, 102);">{</span>
            <span class="k" style="color: rgb(170, 34, 255); font-weight: bold;">return</span><span class="o" style="color: rgb(102, 102, 102);">;</span>
        <span class="o" style="color: rgb(102, 102, 102);">}</span>

        <span class="n">className</span> <span class="o" style="color: rgb(102, 102, 102);">=</span> <span class="n">className</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">replace</span><span class="o" style="color: rgb(102, 102, 102);">(</span><span class="s" style="color: rgb(187, 68, 68);">"android.widget."</span><span class="o" style="color: rgb(102, 102, 102);">,</span> <span class="s" style="color: rgb(187, 68, 68);">""</span><span class="o" style="color: rgb(102, 102, 102);">);</span>
        <span class="n">className</span> <span class="o" style="color: rgb(102, 102, 102);">=</span> <span class="n">className</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">replace</span><span class="o" style="color: rgb(102, 102, 102);">(</span><span class="s" style="color: rgb(187, 68, 68);">"android.view."</span><span class="o" style="color: rgb(102, 102, 102);">,</span> <span class="s" style="color: rgb(187, 68, 68);">""</span><span class="o" style="color: rgb(102, 102, 102);">);</span>
        <span class="n">StringBuilder</span> <span class="n">builder</span> <span class="o" style="color: rgb(102, 102, 102);">=</span> <span class="k" style="color: rgb(170, 34, 255); font-weight: bold;">new</span> <span class="n">StringBuilder</span><span class="o" style="color: rgb(102, 102, 102);">();</span>
        <span class="n">builder</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">append</span><span class="o" style="color: rgb(102, 102, 102);">(</span><span class="sc" style="color: rgb(187, 68, 68);">'('</span><span class="o" style="color: rgb(102, 102, 102);">);</span>
        <span class="n">builder</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">append</span><span class="o" style="color: rgb(102, 102, 102);">(</span><span class="n">index</span><span class="o" style="color: rgb(102, 102, 102);">);</span>
        <span class="n">builder</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">append</span><span class="o" style="color: rgb(102, 102, 102);">(</span><span class="s" style="color: rgb(187, 68, 68);">") "</span><span class="o" style="color: rgb(102, 102, 102);">);</span>
        <span class="n">builder</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">append</span><span class="o" style="color: rgb(102, 102, 102);">(</span><span class="n">className</span><span class="o" style="color: rgb(102, 102, 102);">);</span>
        <span class="k" style="color: rgb(170, 34, 255); font-weight: bold;">if</span> <span class="o" style="color: rgb(102, 102, 102);">(!</span><span class="n">text</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">isEmpty</span><span class="o" style="color: rgb(102, 102, 102);">())</span> <span class="o" style="color: rgb(102, 102, 102);">{</span>
            <span class="n">builder</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">append</span><span class="o" style="color: rgb(102, 102, 102);">(</span><span class="sc" style="color: rgb(187, 68, 68);">':'</span><span class="o" style="color: rgb(102, 102, 102);">);</span>
            <span class="n">builder</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">append</span><span class="o" style="color: rgb(102, 102, 102);">(</span><span class="n">text</span><span class="o" style="color: rgb(102, 102, 102);">);</span>
        <span class="o" style="color: rgb(102, 102, 102);">}</span>
        <span class="k" style="color: rgb(170, 34, 255); font-weight: bold;">if</span> <span class="o" style="color: rgb(102, 102, 102);">(!</span><span class="n">contentDescription</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">isEmpty</span><span class="o" style="color: rgb(102, 102, 102);">())</span> <span class="o" style="color: rgb(102, 102, 102);">{</span>
            <span class="n">builder</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">append</span><span class="o" style="color: rgb(102, 102, 102);">(</span><span class="s" style="color: rgb(187, 68, 68);">" {"</span><span class="o" style="color: rgb(102, 102, 102);">);</span>
            <span class="n">builder</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">append</span><span class="o" style="color: rgb(102, 102, 102);">(</span><span class="n">contentDescription</span><span class="o" style="color: rgb(102, 102, 102);">);</span>
            <span class="n">builder</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">append</span><span class="o" style="color: rgb(102, 102, 102);">(</span><span class="sc" style="color: rgb(187, 68, 68);">'}'</span><span class="o" style="color: rgb(102, 102, 102);">);</span>
        <span class="o" style="color: rgb(102, 102, 102);">}</span>
        <span class="n">builder</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">append</span><span class="o" style="color: rgb(102, 102, 102);">(</span><span class="sc" style="color: rgb(187, 68, 68);">' '</span><span class="o" style="color: rgb(102, 102, 102);">);</span>
        <span class="n">builder</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">append</span><span class="o" style="color: rgb(102, 102, 102);">(</span><span class="n">bounds</span><span class="o" style="color: rgb(102, 102, 102);">);</span>
        <span class="k" style="color: rgb(170, 34, 255); font-weight: bold;">this</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">mDisplayName</span> <span class="o" style="color: rgb(102, 102, 102);">=</span> <span class="n">builder</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">toString</span><span class="o" style="color: rgb(102, 102, 102);">();</span>

private final Map<String, String> mAttributes = new LinkedHashMap();UiNode 节点下定义一个mAttributes LinkedHashMap,用于存储节点所有key-value属性,如:className、text 、index等等,所以,只需要在这个UiNode 类下添加一个获取xpath方法,如下:

    <span class="kd" style="color: rgb(170, 34, 255); font-weight: bold;">public</span> <span class="n">String</span> <span class="nf" style="color: rgb(0, 160, 0);">getXpath</span><span class="p">(</span><span class="o" style="color: rgb(102, 102, 102);">)</span>
    <span class="o" style="color: rgb(102, 102, 102);">{</span>
        <span class="n">String</span> <span class="n">className</span><span class="o" style="color: rgb(102, 102, 102);">=</span><span class="n">getNodeClassAttribute</span><span class="o" style="color: rgb(102, 102, 102);">();</span>
        <span class="n">String</span> <span class="n">xpath</span><span class="o" style="color: rgb(102, 102, 102);">=</span><span class="s" style="color: rgb(187, 68, 68);">"//"</span><span class="o" style="color: rgb(102, 102, 102);">+</span><span class="n">className</span><span class="o" style="color: rgb(102, 102, 102);">;</span>
        <span class="n">String</span> <span class="n">text</span> <span class="o" style="color: rgb(102, 102, 102);">=</span> <span class="n">getAttribute</span><span class="o" style="color: rgb(102, 102, 102);">(</span><span class="s" style="color: rgb(187, 68, 68);">"text"</span><span class="o" style="color: rgb(102, 102, 102);">);</span>
        <span class="k" style="color: rgb(170, 34, 255); font-weight: bold;">if</span><span class="o" style="color: rgb(102, 102, 102);">(</span><span class="n">text</span> <span class="o" style="color: rgb(102, 102, 102);">!=</span><span class="kc" style="color: rgb(170, 34, 255); font-weight: bold;">null</span><span class="o" style="color: rgb(102, 102, 102);">&&</span> <span class="o" style="color: rgb(102, 102, 102);">!</span><span class="n">text</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">equals</span><span class="o" style="color: rgb(102, 102, 102);">(</span><span class="s" style="color: rgb(187, 68, 68);">""</span><span class="o" style="color: rgb(102, 102, 102);">))</span>
        <span class="o" style="color: rgb(102, 102, 102);">{</span>
            <span class="n">xpath</span> <span class="o" style="color: rgb(102, 102, 102);">+=</span> <span class="s" style="color: rgb(187, 68, 68);">"[@text='"</span><span class="o" style="color: rgb(102, 102, 102);">+</span><span class="n">text</span><span class="o" style="color: rgb(102, 102, 102);">+</span><span class="s" style="color: rgb(187, 68, 68);">"']"</span><span class="o" style="color: rgb(102, 102, 102);">;</span>
            <span class="k" style="color: rgb(170, 34, 255); font-weight: bold;">return</span> <span class="n">xpath</span><span class="o" style="color: rgb(102, 102, 102);">;</span>
        <span class="o" style="color: rgb(102, 102, 102);">}</span><span class="k" style="color: rgb(170, 34, 255); font-weight: bold;">else</span> 
        <span class="o" style="color: rgb(102, 102, 102);">{</span>
            <span class="k" style="color: rgb(170, 34, 255); font-weight: bold;">return</span> <span class="n">getAttribute</span><span class="o" style="color: rgb(102, 102, 102);">(</span><span class="s" style="color: rgb(187, 68, 68);">"content-desc"</span><span class="o" style="color: rgb(102, 102, 102);">)</span> <span class="o" style="color: rgb(102, 102, 102);">!=</span><span class="s" style="color: rgb(187, 68, 68);">""</span><span class="o" style="color: rgb(102, 102, 102);">?</span>
                    <span class="n">xpath</span><span class="o" style="color: rgb(102, 102, 102);">+</span><span class="s" style="color: rgb(187, 68, 68);">"[@content-desc='"</span><span class="o" style="color: rgb(102, 102, 102);">+</span><span class="n">getAttribute</span><span class="o" style="color: rgb(102, 102, 102);">(</span><span class="s" style="color: rgb(187, 68, 68);">"content-desc"</span><span class="o" style="color: rgb(102, 102, 102);">)+</span><span class="s" style="color: rgb(187, 68, 68);">"']"</span>
                    <span class="o" style="color: rgb(102, 102, 102);">:</span><span class="n">xpath</span><span class="o" style="color: rgb(102, 102, 102);">+</span><span class="s" style="color: rgb(187, 68, 68);">"[@index='"</span><span class="o" style="color: rgb(102, 102, 102);">+</span><span class="n">getAttribute</span><span class="o" style="color: rgb(102, 102, 102);">(</span><span class="s" style="color: rgb(187, 68, 68);">"index"</span><span class="o" style="color: rgb(102, 102, 102);">)+</span><span class="s" style="color: rgb(187, 68, 68);">"']"</span><span class="o" style="color: rgb(102, 102, 102);">;</span>
        <span class="o" style="color: rgb(102, 102, 102);">}</span>


    <span class="o" style="color: rgb(102, 102, 102);">}</span>

根据约定的优先级,进行筛选(text>content-desc>index),方法定义完毕后,如何触发getXpath()代码呢?
同样,在此包下UiHierarchyXmlLoader.java中,该类是用于处理把dump xml转换为BasicTreeNode对象,UiHierarchyXmlLoader 引用org.xml.sax.helpers处理基本xml文件(Default base class for SAX2 event handlers.),实现了ContentHandler接口下的startElement、endElement接口,

            <span class="kd" style="color: rgb(170, 34, 255); font-weight: bold;">public</span> <span class="kt" style="color: rgb(0, 187, 0); font-weight: bold;">void</span> <span class="nf" style="color: rgb(0, 160, 0);">endElement</span><span class="p">(</span><span class="n">String</span> <span class="n">uri</span><span class="o" style="color: rgb(102, 102, 102);">,</span> <span class="n">String</span> <span class="n">localName</span><span class="o" style="color: rgb(102, 102, 102);">,</span> <span class="n">String</span> <span class="n">qName</span><span class="o" style="color: rgb(102, 102, 102);">)</span>
                    <span class="kd" style="color: rgb(170, 34, 255); font-weight: bold;">throws</span> <span class="n">SAXException</span> <span class="o" style="color: rgb(102, 102, 102);">{</span>
                <span class="k" style="color: rgb(170, 34, 255); font-weight: bold;">if</span> <span class="o" style="color: rgb(102, 102, 102);">(</span><span class="k" style="color: rgb(170, 34, 255); font-weight: bold;">this</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">mParentNode</span> <span class="o" style="color: rgb(102, 102, 102);">!=</span> <span class="kc" style="color: rgb(170, 34, 255); font-weight: bold;">null</span><span class="o" style="color: rgb(102, 102, 102);">)</span> <span class="o" style="color: rgb(102, 102, 102);">{</span>
                    <span class="k" style="color: rgb(170, 34, 255); font-weight: bold;">this</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">mWorkingNode</span> <span class="o" style="color: rgb(102, 102, 102);">=</span> <span class="k" style="color: rgb(170, 34, 255); font-weight: bold;">this</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">mParentNode</span><span class="o" style="color: rgb(102, 102, 102);">;</span>
                    <span class="k" style="color: rgb(170, 34, 255); font-weight: bold;">this</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">mParentNode</span> <span class="o" style="color: rgb(102, 102, 102);">=</span> <span class="k" style="color: rgb(170, 34, 255); font-weight: bold;">this</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">mParentNode</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">getParent</span><span class="o" style="color: rgb(102, 102, 102);">();</span>
                    <span class="err">`</span><span class="n">mTmpNode</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">addAtrribute</span><span class="o" style="color: rgb(102, 102, 102);">(</span><span class="s" style="color: rgb(187, 68, 68);">"xpath"</span><span class="o" style="color: rgb(102, 102, 102);">,</span><span class="n">mTmpNode</span><span class="o" style="color: rgb(102, 102, 102);">.</span><span class="na" style="color: rgb(187, 68, 68);">getXpath</span><span class="o" style="color: rgb(102, 102, 102);">());</span><span class="err">`</span>
                <span class="o" style="color: rgb(102, 102, 102);">}</span>
            <span class="o" style="color: rgb(102, 102, 102);">}</span>
        <span class="o" style="color: rgb(102, 102, 102);">};</span>

到这里,xpath就会自动出现在uiautomatorviewer界面上了,效果如下:


补充
其实uiautomatorviewer二次开发还不止这些,我们可以在uiautomatorviewer中加入录制自动生成java、python等appium脚本,还可以每次用户点击uiautomatorviewer界面,同步刷新(目前需要用户手动点击device screenshot 这个action)等等,如下:

第一次发分享贴,写的不好,希望大家多多鼓励,后续会继续分享

 本帖已被设为精华帖!
共收到  49 条回复
1902
sunrise ·  #1 · 22 小时前  喜欢 

顶,终于分享出来了

1011
doctorq ·  #2 · 22 小时前  喜欢 

赞一个

3010
james88233 ·  #3 · 22 小时前  喜欢 

赞 也就是说即便开发没有定义控件的text description或者device ID,我们依然可以对这个控件进行唯一性的自主命名对吗?

523
cpfeng0124 ·  #4 · 22 小时前  喜欢 

#3楼 @james88233 一开始,想做成你说这个思路的,完全通过布局如://FrameLayout/View/TextView类似这样的方式,后来发现这样做,如果app布局一旦做了调整,解析出来的xpath就不能复用,所以,后来就改成了一个控件,按照text>content-desc>index的优先级进行唯一定位,如:有text内容的,优先,其次是content-desc

3233
snake ·  #5 · 22 小时前  喜欢 

最近也在想弄这种比较快速定位的东东,没想到你先弄出来了。

118
monkey ·  #6 · 22 小时前  喜欢 

可以的。我支持你

3010
james88233 ·  #7 · 22 小时前  喜欢 

#4楼 @cpfeng0124 首先我明白你关于不能复用的那个解释,然后我不太理解的是你接下来说的这个操作,如果uiautomatorviewer可以获取到控件的text desc等信息,我还要xpath的信息做什么呢?

523
cpfeng0124 ·  #8 · 22 小时前  喜欢 

#7楼 @james88233 我们这边遇到这种问题,2个控件,id、text都一样,只有desc或者index不一样,这个时候,就需要些xpath来区分这2个控件了

3500
yuwuhen333 ·  #9 · 21 小时前  喜欢 

#8楼 @cpfeng0124 谢谢分享,如果能上传uiautomatorviewer未修改的源码更棒了

523
cpfeng0124 ·  #10 · 21 小时前  喜欢 

#9楼 @yuwuhen333 uiautomatorviewer本身就是开源的哦,可以通过反编译直接生成代码

3500
yuwuhen333 ·  #11 · 21 小时前  喜欢 

#10楼 @cpfeng0124 反编译是直接解压缩jar包吗?还是需要工具进行反编译

523
cpfeng0124 ·  #12 · 21 小时前  喜欢 

#11楼 @yuwuhen333 z直接解压缩jar包哦

1544
mistyrain ·  #13 · 21 小时前  喜欢 

你好,请教下通过这个进行二次开发,是否也可以解决自定义控件无法定位的问题呢?谢谢~

605
chenhengjie123 ·  #14 · 21 小时前  喜欢 

赞!
有个疑问:如果遇到有两个控件 text 相同,那你生成的 xpath 是否就会一样?

523
cpfeng0124 ·  #15 · 20 小时前  喜欢 

#13楼 @mistyrain 基本可以的唯一定位的,目前通过xpath方式,还没发现定位不到的问题哦

523
cpfeng0124 ·  #16 · 20 小时前  喜欢 

#14楼 @chenhengjie123 吼吼,如果两个text相同的话,生成的xpath确实是一样的哦,有没有啥可以指点一下,如何改进啊?

1105
app_testing ·  #17 · 20 小时前  喜欢 

@chenhengjie123 @cpfeng0124 看他这个代码逻辑只是判断了text是否为空,如果text相同,应该返回的xpath是一致的

523
cpfeng0124 ·  #18 · 20 小时前  喜欢 

#17楼 @app_testing 是的。

1105
app_testing ·  #19 · 20 小时前  喜欢 

@cpfeng0124 ,如果是自定义控件的话,请问有没有思路呢,有人提议用HierachyViewer,但是感觉这个速度比较慢

3500
yuwuhen333 ·  #20 · 20 小时前  喜欢 

#12楼 @cpfeng0124 压缩之后,文件夹中没有com.android.uiautomator.tree也没有UiNode.java,我的是Android19

3010
james88233 ·  #21 · 20 小时前  喜欢 

#8楼 @cpfeng0124 我现在用的工具是黑盒的,所以我也不太清楚具体的架构,当你说的这种情况出现的时候,我们一般就用index=1 或者index=2来区分了 但是面对上图那种钟表式样的控件,就无能为力了,因为基本上text desc这些都没有,classname相同的一大堆,如果可以用xpath编译的话还是挺方便的,不过可能需要增加一些封装,让这个操作傻瓜化,比如tester点击控件可以直接对其命名

523
cpfeng0124 ·  #22 · 20 小时前  喜欢 

#19楼 @app_testing HierachyViewer我之前也做过,思路基本差不多,当时的需求是,需要测试手机整机全系统测试:如打电话、发短型、甚至并发,打电话的时候,上网啥的,当时就是基于HierachyViewer二次开发,全系统自动化测试的

605
chenhengjie123 ·  #23 · 20 小时前  喜欢 

#16楼 @cpfeng0124 大致思路:
在 dump 出来的 xml 文件中使用生成的 xpath 来查找节点,如果找到不止一个节点,就加上更多属性匹配条件(同样按照 text->content-desc->index,例如 android.view.view[@text="a" and @content-desc="b" and @index="1"])。如果都加上后还是不止一个元素,直接给个绝对路径吧。

可以录制脚本这个太赞了!能分享一下思路不?

1105
app_testing ·  #24 · 20 小时前  喜欢 

@cpfeng0124 我的意思是HierachyViewer这个速度比较慢,你对这种情况有没有别的解决思路

523
cpfeng0124 ·  #25 · 20 小时前  喜欢 

#23楼 @chenhengjie123 录制脚本这个,我打算下周再做一次移动录制生成代码,因为第一次写这些分享,效率有点低啊,吼吼,谢谢你的指点!

523
cpfeng0124 ·  #26 · 20 小时前  喜欢 

#24楼 @app_testing 速度比较慢,主要卡在dump这个动作需要一定时间,这个我也在研究哦,吼吼!有啥想法,记得分享一下哈

605
chenhengjie123 ·  #27 · 20 小时前  喜欢 

#25楼 @cpfeng0124 期待你的分享!

3500
yuwuhen333 ·  #28 · 19 小时前  喜欢 

#12楼 @cpfeng0124 能不能具体讲下怎么进行反编译,我的uiautomator.jar解压缩后,目录结构如下图


一直没看到你说的修改的文件

523
cpfeng0124 ·  #29 · 19 小时前  1 人喜欢 

#28楼 @yuwuhen333 你找错包啦,是这个uiautomatorviewer.jar包,在sdk\tools\lib\uiautomatorviewer.jar 这个目录下哦

3500
yuwuhen333 ·  #30 · 19 小时前  喜欢 

#29楼 @cpfeng0124 太感谢了,顺便再问一句解压缩之后,按照文章中的说明进行修改后,怎么重新打包成jar?

F03f3ad74e9900a194086c25ebf4bb38
xxfcxx ·  #31 · 19 小时前  喜欢 

不好意思,楼主,您的源码能share一下么

523
cpfeng0124 ·  #32 · 18 小时前  喜欢 

#30楼 @yuwuhen333 正常打包就ok啦,要么导出来jar来,要么用maven打包

523
cpfeng0124 ·  #33 · 18 小时前  喜欢 

#31楼 @xxfcxx 额,这个源码暂时不能开放哦,因为里面还有其他功能,不止这个简单的xpath哦

3500
yuwuhen333 ·  #34 · 18 小时前  喜欢 

#32楼 @cpfeng0124 将uinode变为java类型添加你的代码后,在转换为uinode.class报错了UiNode.java:123: 错误: 非法的 Unicode 转义,继续调整。能不能先把你修改成生成的uiautomatorviewer.jar分享让我们感受下

F03f3ad74e9900a194086c25ebf4bb38
xxfcxx ·  #35 · 16 小时前  喜欢 

楼主能否详细说明下 如何反编译 jar包,并修改相关源文件,然后再打包的这个过程呢,多谢了

3500
yuwuhen333 ·  #36 · 15 小时前  喜欢 

#32楼 @cpfeng0124 在配置UINode.java.中一直报String className=getNodeClassAttribute();找不到这个方法,这个方法是从哪里来的啊

523
cpfeng0124 ·  #37 · 15 小时前  喜欢 

#36楼 @yuwuhen333 getNodeClassAttribute(),这个方法,需要你自己添加一下哦,骚瑞啊,没说清楚,getNodeClassAttribute这个方法很简单,就返回private final Map<String, String> mAttributes = new LinkedHashMap();这里定义的mAttributes即可

3500
yuwuhen333 ·  #38 · 15 小时前  喜欢 

#37楼 @cpfeng0124 能不能贴出来下?mTmpNode.addAtrribute("xpath",mTmpNode.getXpath());这个为什么在两边还有符号呢

F03f3ad74e9900a194086c25ebf4bb38
xxfcxx ·  #39 · 15 小时前  喜欢 

#37楼 @cpfeng0124 不是返回这个map吧,String className=getNodeClassAttribute(), 这是String类型啊

F03f3ad74e9900a194086c25ebf4bb38
xxfcxx ·  #40 · 15 小时前  喜欢 

#37楼 @cpfeng0124 是不是 要private String getNodeClassAttribute() {
// TODO Auto-generated method stub
return mAttributes.get("className");
}

523
cpfeng0124 ·  #41 · 15 小时前  喜欢 

#40楼 @xxfcxx 是的

3500
yuwuhen333 ·  #42 · 15 小时前  喜欢 

#40楼 @xxfcxx 我写的是return this.mAttributes.get("class");

3500
yuwuhen333 ·  #43 · 15 小时前  喜欢 

#41楼 @cpfeng0124 public void endElement(String uri, String localName, String qName)
throws SAXException {
if (this.mParentNode != null) {
this.mWorkingNode = this.mParentNode;
this.mParentNode = this.mParentNode.getParent();
mTmpNode.addAtrribute("xpath",mTmpNode.getXpath());
}
}
};这个代码中你加的那行为什么多了``呢

F03f3ad74e9900a194086c25ebf4bb38
xxfcxx ·  #44 · 15 小时前  喜欢 

我按照你这里改完之后,重新打成uiautomator.jar,然后启动,提示 uiautomatorviewer.jar中没有主清单属性

3500
yuwuhen333 ·  #45 · 15 小时前  喜欢 

#44楼 @xxfcxx UiHierarchyXmlLoader.java你修改好了吗

F03f3ad74e9900a194086c25ebf4bb38
xxfcxx ·  #46 · 15 小时前  喜欢 

#45楼 @yuwuhen333 我修改了,但是在eclipse里一直报错,我是改完之后export jar,然后从jar包里copy这2个class文件,然后复制到uiautomator.jar解压后的文件夹,然后jar cvf打包。。。

3500
yuwuhen333 ·  #47 · 14 小时前  喜欢 

#46楼 @xxfcxx 相同的提示,我也是修改UiHierarchyXmlLoader.java的时候各种错误
#41楼 @cpfeng0124 能不能提供个你只是添加了xpath功能的jar包出来?

F03f3ad74e9900a194086c25ebf4bb38
xxfcxx ·  #48 · 11 小时前  喜欢 

mTmpNode 这个变量是哪来的,源码没有啊,是你加的一个成员变量么,有没有初始化啊

3500
yuwuhen333 ·  #49 · 11 小时前  喜欢 

#37楼 @cpfeng0124 能不能提供个xpath的uiautomatorviewer.jar?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值