uiautomatorviewer 新增功能 compressed 之 Device 端细节

昨天了解了uiautoamtorviewer新增功能dump --compressed,是一个直接发送到设备端的命令,那么这个命令发送到设备端后,设备端是如何操作的呢?我又成了10万个为什么了?继续源码研究......

源码地址

google source

这个jar包最新的版本只到了4.4.2。说明5.0后的uiautomator设备端是没有改变的,那么说明dump --compressed之前就有,只是我不知道罢鸟。结论:

dump --compressed命令4.4.2时代就有,只是年少无知没发现

源码环境搭建

解压以后项目结果如下所示:

直接用eclipse的import功能导入,整体导入。导入eclipse后,如下图所示,感叹号是因为没有添加android.jar造成的,加上就好了。

源码分析

当我们在命令行下输入下面命令的时候,android系统就会调用cmds目录下的Launcher类中的main方法中

<span class="o">/</span><span class="n">system</span><span class="o">/</span><span class="n">bin</span><span class="o">/</span><span class="n">uiautomator</span> <span class="n">dump</span> <span class="o">--</span><span class="n">compressed</span> <span class="o">/</span><span class="n">data</span><span class="o">/</span><span class="n">local</span><span class="o">/</span><span class="n">tmp</span><span class="o">/</span><span class="n">uidump</span><span class="o">.</span><span class="na">xml</span>
Launcher

所以我们从main开始我们的大餐:

<span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="p">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
        <span class="c1">// show a meaningful process name in `ps`</span>
        <span class="n">Process</span><span class="o">.</span><span class="na">setArgV0</span><span class="o">(</span><span class="s">"uiautomator"</span><span class="o">);</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">args</span><span class="o">.</span><span class="na">length</span> <span class="o">>=</span> <span class="mi">1</span><span class="o">)</span> <span class="o">{</span>
            <span class="n">Command</span> <span class="n">command</span> <span class="o">=</span> <span class="n">findCommand</span><span class="o">(</span><span class="n">args</span><span class="o">[</span><span class="mi">0</span><span class="o">]);</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">command</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
                <span class="n">String</span><span class="o">[]</span> <span class="n">args2</span> <span class="o">=</span> <span class="o">{};</span>
                <span class="k">if</span> <span class="o">(</span><span class="n">args</span><span class="o">.</span><span class="na">length</span> <span class="o">></span> <span class="mi">1</span><span class="o">)</span> <span class="o">{</span>
                    <span class="c1">// consume the first arg</span>
                    <span class="n">args2</span> <span class="o">=</span> <span class="n">Arrays</span><span class="o">.</span><span class="na">copyOfRange</span><span class="o">(</span><span class="n">args</span><span class="o">,</span> <span class="mi">1</span><span class="o">,</span> <span class="n">args</span><span class="o">.</span><span class="na">length</span><span class="o">);</span>
                <span class="o">}</span>
                <span class="n">command</span><span class="o">.</span><span class="na">run</span><span class="o">(</span><span class="n">args2</span><span class="o">);</span>
                <span class="k">return</span><span class="o">;</span>
            <span class="o">}</span>
        <span class="o">}</span>
        <span class="n">HELP_COMMAND</span><span class="o">.</span><span class="na">run</span><span class="o">(</span><span class="n">args</span><span class="o">);</span>
    <span class="o">}</span>

下面一步一步解释上面的代码的意思:
1.首先在进程信息中添加上uiautomator信息,这样你在命令行中敲adb shell ps就能查看到uiautomator进程的信息了。
2.判断参数数量是否大于0,其中要了解的是上面的命令中dump算第一个参数。不要把system/bin/uiautmator当成了第一个参数。
3.当参数数量大于0时,获得第一个参数的值args[0],其中findCommand()方法根据命令的名称得到命令的类型。总共有四个命令:helpeventsruntestdump,你如果想知道各个命令是干什么的,你可以在命令行下敲一下看看输出就知道了。

<span class="kd">private</span> <span class="kd">static</span> <span class="n">Command</span><span class="o">[]</span> <span class="n">COMMANDS</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Command</span><span class="o">[]</span> <span class="o">{</span>
        <span class="n">HELP_COMMAND</span><span class="o">,</span>
        <span class="k">new</span> <span class="n">RunTestCommand</span><span class="o">(),</span>
        <span class="k">new</span> <span class="n">DumpCommand</span><span class="o">(),</span>
        <span class="k">new</span> <span class="n">EventsCommand</span><span class="o">(),</span>
    <span class="o">};</span>

4.如果命令不为空,就要执行相应的命令,但是还要将剩下的参数(可能为空的参数,但不是null值)传入run方法中,让各个类型自己处理。我们的命令是dump命令,所以下一步进入DumpCommand中。
5.如果不带参数的话,直接执行help命令。

DumpCommand
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">run</span><span class="p">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">File</span> <span class="n">dumpFile</span> <span class="o">=</span> <span class="n">DEFAULT_DUMP_FILE</span><span class="o">;</span>
        <span class="kt">boolean</span> <span class="n">verboseMode</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span>

        <span class="k">for</span> <span class="o">(</span><span class="n">String</span> <span class="n">arg</span> <span class="o">:</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">arg</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="s">"--compressed"</span><span class="o">))</span>
                <span class="n">verboseMode</span> <span class="o">=</span> <span class="kc">false</span><span class="o">;</span>
            <span class="k">else</span> <span class="k">if</span> <span class="o">(!</span><span class="n">arg</span><span class="o">.</span><span class="na">startsWith</span><span class="o">(</span><span class="s">"-"</span><span class="o">))</span> <span class="o">{</span>
                <span class="n">dumpFile</span> <span class="o">=</span> <span class="k">new</span> <span class="n">File</span><span class="o">(</span><span class="n">arg</span><span class="o">);</span>
            <span class="o">}</span>
        <span class="o">}</span>

        <span class="n">UiAutomationShellWrapper</span> <span class="n">automationWrapper</span> <span class="o">=</span> <span class="k">new</span> <span class="n">UiAutomationShellWrapper</span><span class="o">();</span>
        <span class="n">automationWrapper</span><span class="o">.</span><span class="na">connect</span><span class="o">();</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">verboseMode</span><span class="o">)</span> <span class="o">{</span>
            <span class="c1">// default</span>
            <span class="n">automationWrapper</span><span class="o">.</span><span class="na">setCompressedLayoutHierarchy</span><span class="o">(</span><span class="kc">false</span><span class="o">);</span>
        <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
            <span class="n">automationWrapper</span><span class="o">.</span><span class="na">setCompressedLayoutHierarchy</span><span class="o">(</span><span class="kc">true</span><span class="o">);</span>
        <span class="o">}</span>

        <span class="c1">// It appears that the bridge needs time to be ready. Making calls to the</span>
        <span class="c1">// bridge immediately after connecting seems to cause exceptions. So let's also</span>
        <span class="c1">// do a wait for idle in case the app is busy.</span>
        <span class="k">try</span> <span class="o">{</span>
            <span class="n">UiAutomation</span> <span class="n">uiAutomation</span> <span class="o">=</span> <span class="n">automationWrapper</span><span class="o">.</span><span class="na">getUiAutomation</span><span class="o">();</span>
            <span class="n">uiAutomation</span><span class="o">.</span><span class="na">waitForIdle</span><span class="o">(</span><span class="mi">1000</span><span class="o">,</span> <span class="mi">1000</span> <span class="o">*</span> <span class="mi">10</span><span class="o">);</span>
            <span class="n">AccessibilityNodeInfo</span> <span class="n">info</span> <span class="o">=</span> <span class="n">uiAutomation</span><span class="o">.</span><span class="na">getRootInActiveWindow</span><span class="o">();</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">info</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
                <span class="n">System</span><span class="o">.</span><span class="na">err</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">"ERROR: null root node returned by UiTestAutomationBridge."</span><span class="o">);</span>
                <span class="k">return</span><span class="o">;</span>
            <span class="o">}</span>

            <span class="n">Display</span> <span class="n">display</span> <span class="o">=</span>
                    <span class="n">DisplayManagerGlobal</span><span class="o">.</span><span class="na">getInstance</span><span class="o">().</span><span class="na">getRealDisplay</span><span class="o">(</span><span class="n">Display</span><span class="o">.</span><span class="na">DEFAULT_DISPLAY</span><span class="o">);</span>
            <span class="kt">int</span> <span class="n">rotation</span> <span class="o">=</span> <span class="n">display</span><span class="o">.</span><span class="na">getRotation</span><span class="o">();</span>
            <span class="n">Point</span> <span class="n">size</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Point</span><span class="o">();</span>
            <span class="n">display</span><span class="o">.</span><span class="na">getSize</span><span class="o">(</span><span class="n">size</span><span class="o">);</span>
            <span class="n">AccessibilityNodeInfoDumper</span><span class="o">.</span><span class="na">dumpWindowToFile</span><span class="o">(</span><span class="n">info</span><span class="o">,</span> <span class="n">dumpFile</span><span class="o">,</span> <span class="n">rotation</span><span class="o">,</span> <span class="n">size</span><span class="o">.</span><span class="na">x</span><span class="o">,</span> <span class="n">size</span><span class="o">.</span><span class="na">y</span><span class="o">);</span>
        <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">TimeoutException</span> <span class="n">re</span><span class="o">)</span> <span class="o">{</span>
            <span class="n">System</span><span class="o">.</span><span class="na">err</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">"ERROR: could not get idle state."</span><span class="o">);</span>
            <span class="k">return</span><span class="o">;</span>
        <span class="o">}</span> <span class="k">finally</span> <span class="o">{</span>
            <span class="n">automationWrapper</span><span class="o">.</span><span class="na">disconnect</span><span class="o">();</span>
        <span class="o">}</span>
        <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span>
                <span class="n">String</span><span class="o">.</span><span class="na">format</span><span class="o">(</span><span class="s">"UI hierchary dumped to: %s"</span><span class="o">,</span> <span class="n">dumpFile</span><span class="o">.</span><span class="na">getAbsolutePath</span><span class="o">()));</span>
    <span class="o">}</span>

run方法执行的步骤有点长,没关系,慢慢来。
1.首先创建文件用来保存dump下来的信息,这个时候需要注意getLegacyExternalStorageDirectory是个隐藏的方法,官网上的api没有这个方法的解释,可以在源码上找到,我贴在这里,帮助理解,该文件的路径为/storage/emulated/legacy/window_dump.xml

<span class="cm">/** {@hide} */</span>
    <span class="kd">public</span> <span class="kd">static</span> <span class="n">File</span> <span class="n">getLegacyExternalStorageDirectory</span><span class="o">()</span> <span class="o">{</span>
        <span class="k">return</span> <span class="k">new</span> <span class="n">File</span><span class="o">(</span><span class="n">System</span><span class="o">.</span><span class="na">getenv</span><span class="o">(</span><span class="n">ENV_EXTERNAL_STORAGE</span><span class="o">));</span>
    <span class="o">}</span>

2.然后解析传入的参数得到保存的路径以及是否压缩。
3.然后创建UiAutomationShellWrapper对象,启动Handler线程,创建Uiautomation对象,并建立连接。然后设置了压缩属性。这个UiAutomationShellWrapper也是隐藏的,也只能到源码环境下查看。
4.然后我们得到了Uiautomation的对象实例

<span class="n">UiAutomation</span> <span class="n">uiAutomation</span> <span class="o">=</span> <span class="n">automationWrapper</span><span class="o">.</span><span class="na">getUiAutomation</span><span class="o">();</span>
            <span class="n">uiAutomation</span><span class="o">.</span><span class="na">waitForIdle</span><span class="o">(</span><span class="mi">1000</span><span class="o">,</span> <span class="mi">1000</span> <span class="o">*</span> <span class="mi">10</span><span class="o">);</span>
            <span class="n">AccessibilityNodeInfo</span> <span class="n">info</span> <span class="o">=</span> <span class="n">uiAutomation</span><span class="o">.</span><span class="na">getRootInActiveWindow</span><span class="o">();</span>

等待UI界面处于稳定后(idle状态),然后我们调用getRootInActiveWindow方法获得结果的根节点。这个时候我们整个流程差不多结束了,我们care的--compressed还没看到。

compressed

经过一路追踪,发现compressed属性在整个过程中的作用是给AccessibilityServiceInfo对象添加了一个 FLAG_INCLUDE_NOT_IMPORTANT_VIEWS标志位。其他的就是正常获取dump信息流程,这个标志位对获取信息时候的影响有多大,留到以后来解释(没有源码环境,不好调试啊,头疼......)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值