新版本的uiautomatorviewer
工具中新增了dump --compressed
功能,可以选择只获取界面中呈现在你面前的控件,非常精简。那么它是怎么做到的,是更换命令了么?还是在dump
的基础上在本地做了一个优化呢?速度怎么样?我带着这些疑惑开始了源码分析之路。
下载源码
配置源码环境
解压缩有项目结构如下:
用你熟悉的方式将源码配置IDE中,我用的是eclipse具体配置过程如下:
虽然它有build.gradle
,但是我用这个往eclipse
里导入gradle
依然没成功,没关系,用暴力的方式。直接创建一个gradle
项目,然后将源码copy
进去(原谅我的无知),创建的过程看gradle学习(21)-在eclipse中构建java项目 。导入以后肯定有很多错误,这是缺少jar包造成的,添加如下jar
包就行了:
所有jar包都可以在你的sdk目录下的tools/lib
中找到,其中需要注意一点的是swt.jar
包在以x86
开始的文件夹中,选择对应平台的jar
包就行。
等这些都完成以后,代码中的x号就会消失,项目结构如下:
这个时候在UiAutomatorViewer
类中启动main方法,会出现我们熟悉的uiautomatorviewer
工具界面:
这个时候你点击Device screentshot
按钮会报错:
这是因为uiautomatorviewer项目是根据com.android.uiautomator.bindir
属性去找sdk路径的,这就要求你启动的时候需要去添加这个属性。打开DebugBridge
类可以看到getAdbLocation()方法中代码:
<span class="n">String</span> <span class="n">toolsDir</span> <span class="o">=</span> <span class="n">System</span><span class="o">.</span><span class="na">getProperty</span><span class="o">(</span><span class="s">"com.android.uiautomator.bindir"</span><span class="o">);</span> <span class="c1">//$NON-NLS-1$</span>
<span class="k">if</span> <span class="o">(</span><span class="n">toolsDir</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="kc">null</span><span class="o">;</span>
<span class="o">}</span>
<span class="n">File</span> <span class="n">sdk</span> <span class="o">=</span> <span class="k">new</span> <span class="n">File</span><span class="o">(</span><span class="n">toolsDir</span><span class="o">).</span><span class="na">getParentFile</span><span class="o">();</span>
<span class="c1">// check if adb is present in platform-tools</span>
<span class="n">File</span> <span class="n">platformTools</span> <span class="o">=</span> <span class="k">new</span> <span class="n">File</span><span class="o">(</span><span class="n">sdk</span><span class="o">,</span> <span class="s">"platform-tools"</span><span class="o">);</span>
<span class="n">File</span> <span class="n">adb</span> <span class="o">=</span> <span class="k">new</span> <span class="n">File</span><span class="o">(</span><span class="n">platformTools</span><span class="o">,</span> <span class="n">SdkConstants</span><span class="o">.</span><span class="na">FN_ADB</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span class="n">adb</span><span class="o">.</span><span class="na">exists</span><span class="o">())</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">adb</span><span class="o">.</span><span class="na">getAbsolutePath</span><span class="o">();</span>
<span class="o">}</span>
<span class="c1">// check if adb is present in the tools directory</span>
<span class="n">adb</span> <span class="o">=</span> <span class="k">new</span> <span class="n">File</span><span class="o">(</span><span class="n">toolsDir</span><span class="o">,</span> <span class="n">SdkConstants</span><span class="o">.</span><span class="na">FN_ADB</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span class="n">adb</span><span class="o">.</span><span class="na">exists</span><span class="o">())</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">adb</span><span class="o">.</span><span class="na">getAbsolutePath</span><span class="o">();</span>
<span class="o">}</span>
<span class="c1">// check if we're in the Android source tree where adb is in $ANDROID_HOST_OUT/bin/adb</span>
<span class="n">String</span> <span class="n">androidOut</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="s">"ANDROID_HOST_OUT"</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span class="n">androidOut</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="n">adbLocation</span> <span class="o">=</span> <span class="n">androidOut</span> <span class="o">+</span> <span class="n">File</span><span class="o">.</span><span class="na">separator</span> <span class="o">+</span> <span class="s">"bin"</span> <span class="o">+</span> <span class="n">File</span><span class="o">.</span><span class="na">separator</span> <span class="o">+</span>
<span class="n">SdkConstants</span><span class="o">.</span><span class="na">FN_ADB</span><span class="o">;</span>
<span class="k">if</span> <span class="o">(</span><span class="k">new</span> <span class="n">File</span><span class="o">(</span><span class="n">adbLocation</span><span class="o">).</span><span class="na">exists</span><span class="o">())</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">adbLocation</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="k">return</span> <span class="kc">null</span><span class="o">;</span>
你可以在main
方法中加上这个属性,通过System.setProperty(key, value)
来设置,key就是上面com.android.uiautomator.bindir
,value
值就是你的sdk路径,当然这种方式是不好的,换路径了还得再改,这个很不好的。当然我们不必鸟它,我们采用cts的方式。将返回null
值的地方全部用adb
代替,代码修改如下:
<span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="n">String</span> <span class="n">ADB_COMMAND</span> <span class="o">=</span> <span class="s">"adb"</span><span class="o">;</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="n">String</span> <span class="n">getAdbLocation</span><span class="o">()</span> <span class="o">{</span>
<span class="n">String</span> <span class="n">toolsDir</span> <span class="o">=</span> <span class="n">System</span><span class="o">.</span><span class="na">getProperty</span><span class="o">(</span><span class="s">"com.android.uiautomator.bindir"</span><span class="o">);</span> <span class="c1">//$NON-NLS-1$</span>
<span class="k">if</span> <span class="o">(</span><span class="n">toolsDir</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">ADB_COMMAND</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">......</span>
<span class="k">return</span> <span class="n">ADB_COMMAND</span><span class="o">;</span>
<span class="o">}</span>
好,这个时候我们再次点击Device screenshot
按钮,就可以像往常一样得到设备的界面了和控件信息了:
开始调试
源码搭建完成,就可以进入主题了,开始研究源码了,在ScreenshotAction
类中的run
方法里打上断点,以Debug As
启动应用程序,然后点击Device screentshot with Compressed
进入Debug
界面。第一行代码自然是我们断点的地方,因为我们主要看dump --compressed
这个功能点,其他地方我就不多做介绍了,我加快速度了,直接进入正题,一路debug
到了UiAutomatorHelper
的静态方法getUiHierarchyFile(IDevice device, File dst, IProgressMonitor monitor, boolean compressed)
中,看重要代码(如果你debug
不过来,你直接把断点打在下面的代码块就行了):
<span class="kd">private</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">getUiHierarchyFile</span><span class="p">(</span><span class="n">IDevice</span> <span class="n">device</span><span class="o">,</span> <span class="n">File</span> <span class="n">dst</span><span class="o">,</span>
<span class="n">IProgressMonitor</span> <span class="n">monitor</span><span class="o">,</span> <span class="kt">boolean</span> <span class="n">compressed</span><span class="o">)</span> <span class="o">{</span>
<span class="o">......</span>
<span class="n">monitor</span><span class="o">.</span><span class="na">subTask</span><span class="o">(</span><span class="s">"Taking UI XML snapshot..."</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span class="n">compressed</span><span class="o">){</span>
<span class="n">command</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">"%s %s --compressed %s"</span><span class="o">,</span> <span class="n">UIAUTOMATOR</span><span class="o">,</span>
<span class="n">UIAUTOMATOR_DUMP_COMMAND</span><span class="o">,</span>
<span class="n">UIDUMP_DEVICE_PATH</span><span class="o">);</span>
<span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
<span class="n">command</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">"%s %s %s"</span><span class="o">,</span> <span class="n">UIAUTOMATOR</span><span class="o">,</span>
<span class="n">UIAUTOMATOR_DUMP_COMMAND</span><span class="o">,</span>
<span class="n">UIDUMP_DEVICE_PATH</span><span class="o">);</span>
<span class="o">}</span>
<span class="n">CountDownLatch</span> <span class="n">commandCompleteLatch</span> <span class="o">=</span> <span class="k">new</span> <span class="n">CountDownLatch</span><span class="o">(</span><span class="mi">1</span><span class="o">);</span>
<span class="k">try</span> <span class="o">{</span>
<span class="n">CountDownLatch</span> <span class="n">commandCompleteLatch</span> <span class="o">=</span> <span class="k">new</span> <span class="n">CountDownLatch</span><span class="o">(</span><span class="mi">1</span><span class="o">);</span>
<span class="n">device</span><span class="o">.</span><span class="na">executeShellCommand</span><span class="o">(</span><span class="n">command</span><span class="o">,</span>
<span class="k">new</span> <span class="n">CollectingOutputReceiver</span><span class="o">(</span><span class="n">commandCompleteLatch</span><span class="o">));</span>
<span class="n">commandCompleteLatch</span><span class="o">.</span><span class="na">await</span><span class="o">(</span><span class="mi">5</span><span class="o">,</span> <span class="n">TimeUnit</span><span class="o">.</span><span class="na">SECONDS</span><span class="o">);</span>
<span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">Exception</span> <span class="n">e1</span><span class="o">)</span> <span class="o">{</span>
<span class="c1">// ignore exceptions while deleting stale files</span>
<span class="o">}</span>
<span class="o">......</span>
<span class="o">}</span>
执行的命令我们得到
<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>
那第一个问题解决了,就是这是一个新命令dump --compressed
(之前没用过,不知道是不是最新的)。我们获得的简洁版的控件信息也不是在本地优化的,是直接在server
端传过来就是这样的,这样速度会提高不少。
adb命令实验
C:\Users\hui.qian>adb shell /system/bin/uiautomator dump --compressed /data/loca
l/tmp/uidump.xml
UI hierchary dumped to: /data/local/tmp/uidump.xml
C:\Users\hui.qian>adb pull /data/local/tmp/uidump.xml c:\
394 KB/s (6308 bytes in 0.015s)
C:\Users\hui.qian>adb shell /system/bin/uiautomator dump /data/local/tmp/uidump.
xml
UI hierchary dumped to: /data/local/tmp/uidump.xml
C:\Users\hui.qian>adb pull /data/local/tmp/uidump.xml c:\1.xml
1363 KB/s (21778 bytes in 0.015s)
将上面2个文件进行比较就会发现简洁不少。