Android工具HierarchyViewer 代码导读(3) -- 后台代码

在上文中,我们讲解了如何把HierarchyViewer的项目导入到Eclipse中,以便更高效阅读代码。本文将讲解HierarchyViewer的后台代码,建议大家可以先阅读<Android工具HierarchyViewer代码导读(1) -- 功能实现演示>一文, 其中的代码演示了HierarchyViewer的主要功能。而本文就是讲解HierarchyViewer是如何实现功能的。

 

把复杂的代码讲解清楚一般都不是很容易的事情,为了不把本文写成流水帐,文章将尽量集中在HierarchyViewer后台代码的主要脉络上,许多细节需要读者自己去阅读,那是必须的。

 

MVC模式

HierarchyViewer采用典型的MVC模式设计。

当打开HierarchyViewer,进入主界面时,其对应的MVC模式是:HierarchyViewerDirector.java是Controller,DeviceSelectionModel.java是Model,DeviceSelector是View,如下图所示:

image

 

当双击某个Acitivity,进入浏览层次图界面时,其对应的MVC模式是:HierarchyViewerDirector.java是Controller,TreeViewModel.java是Model,Views是TreeViewController.java、TreeViewOverview.java、PropertyViewer.java、TreeViewer.java、LayoutViewer.java:

image

 

HierachyViewerDirector.java(即Controller)通过DeviceBridge.java来和Android设备通信,而DeviceBridge.java具体是通过AndroidDebugBridage.java和DeviceConnection.java来和设备通信。如下图所示:

image

 

AndroidDebugBridge.java : AndroidDebugBridge.java是ADB API,位于ddmlib项目中。 它实现了命令行版adb一样的功能,在HierarchyViewer中主要用到其连接设备,forward端口,启动ViewServer等操作。

DeviceConnection.java: 负责和ViewServer通信,向ViewServer发送命令并接受其返回的信息。从而获取Activity列表、控件层次结构图、截图等。

 

入口点

后台代码的入口点在HierarchyViewerApplication.java的createContents method中:

@Overrideprotected Control createContents(Composite parent) {// create this only once the window is opened to please SWT on MacmDirector = HierarchyViewerApplicationDirector.createDirector();mDirector.initDebugBridge();mDirector.startListenForDevices();mDirector.populateDeviceSelectionModel();//... ...}

以上代码做了如下工作:

1,HierarchyViewerApplicationDirector.createDirector() -- 创建一个HierarchyViewerDirector对象

2,mDirector.initDebugBridge() -- 初始化AndroidDebugBridge

3,mDirector.startListenForDevices() -- 把mDirctor注册为AndroidDebugBridge的监听者(HierarchyViewerDirector继承了IDeviceChangeListener接口),当有设备连接、断开、改变时,mDirctor将接收到事件。

4,mDirector.populateDeviceSelectionModel() -- 获取当前已经连接的设备列表,处理并显示它们。

 

阅读populateDeviceSelectionModel()函数你会发现, 其中获取到当前已经连接的所有设备列表后,是通过deviceConnected函数来“处理”这些设备;当有新设备连接触发设备连接事件时,也是通过deviceConnected函数来“处理”它。

 

启动并连接设备的ViewServer,获取Activities并显示列表

HierarchyViewerDirector的deviceConnected 方法,是对IDeviceChangeListener接口方法的实现,我们来看它是如何“处理”一台和adb建立连接的设备的:

    public void deviceConnected(final IDevice device) {executeInBackground("Connecting device", new Runnable() {public void run() {if (DeviceSelectionModel.getModel().containsDevice(device)) {windowsChanged(device);} else if (device.isOnline()) {DeviceBridge.setupDeviceForward(device);if (!DeviceBridge.isViewServerRunning(device)) {if (!DeviceBridge.startViewServer(device)) {// Let's do something interesting here... Try again// in 2 seconds.try {Thread.sleep(2000);} catch (InterruptedException e) {}if (!DeviceBridge.startViewServer(device)) {Log.e(TAG, "Unable to debug device " + device);DeviceBridge.removeDeviceForward(device);} else {loadViewServerInfoAndWindows(device);}return;}}loadViewServerInfoAndWindows(device);}}});}

在这个方法中做了如下事情:

1)DeviceBridge.setupDeviceForward(device) -- 把该设备的4939端口映射到本地端口。 HierarchyViewer维护一个列表 --sDevicePortMap,它记录哪个设备被映射到了哪个本地端口。

2)DeviceBridge.isViewServerRunning(device) -- 判断该设备的ViewServer是否打开。

3)DeviceBridge.startViewServer(device) -- 打开ViewServer。

4)loadViewServerInfoAndWindows(device) -- 1)获取该设备ViewServer信息,比如版本信息等 2)获取该设备其所有活动的Activities(在HierarchyView源代码中,Activities总是被命名为Windows)。

(如果读者不明白以上函数的意义,再次建议阅读<功能实现演示>)

 

让我们"Step Into”,来看看loadViewServerInfoAndWindows方法:

    private void loadViewServerInfoAndWindows(final IDevice device) {ViewServerInfo viewServerInfo = DeviceBridge.loadViewServerInfo(device);if (viewServerInfo == null) {return;}Window[] windows = DeviceBridge.loadWindows(device);DeviceSelectionModel.getModel().addDevice(device, windows, viewServerInfo);if (viewServerInfo.protocolVersion >= 3) {WindowUpdater.startListenForWindowChanges(HierarchyViewerDirector.this, device);focusChanged(device);}}

 

1,DeviceBridge.loadViewServerInfo(device) -- 读取ViewServer信息。

2,DeviceBridge.loadWindows(device) -- 发送 “LIST”命令给ViewServer,读取设备所有活动的Activities。

3,DeviceSelectionModel.getModel().addDevice(device, windows, viewServerInfo) -- 更新DeviceSelectionModel数据,然后该Model将通过事件通知Views来更新显示。

 

我们到哪了?

在以上代码完成后,HierarchyViewer完成了主界面的加载,已经连接的设备及其活动的Activities显示出来了:

20120729215028656

读取Activity的控件层次图

这时,当用户双击上图中设备的某个Activity,希望查看其控件层次图时,事件(DeviceSelector.java中的widgetDefaultSelected事件)将调用HierarchyViewerDirector.java的loadViewTreeData方法:

    public void loadViewTreeData(final Window window) {executeInBackground("Loading view hierarchy", new Runnable() {public void run() {mFilterText = ""; //$NON-NLS-1$ViewNode viewNode = DeviceBridge.loadWindowData(window);if (viewNode != null) {DeviceBridge.loadProfileData(window, viewNode);viewNode.setViewCount();TreeViewModel.getModel().setData(window, viewNode);}}});}

 

1,DeviceBridge.loadWindowData(window) -- 读取Activity的所有控件信息,并把每个控件的信息构造成一个ViewNode对象,所有的ViewNode组成一个树,该函数的返回值是树的根节点。

2,DeviceBridge.loadProfileData(window, viewNode) -- 遍历整个ViewNode树,为树中的每个节点向ViewServer读取ProfileData。遗憾的是,目前为止我也没有搞明白ProfileData的作用。

3,viewNode.setViewCount() -- 遍历整个ViewNode树,计算每个子树所包含的节点数量,保存在ViewNode的viewCount字段中。

4,TreeViewModel.getModel().setData(window, viewNode) -- 更新TreeViewModel的数据源,该Modell将通知所有监听者 -- TreeViewController.java、TreeViewOverview.java、PropertyViewer.java、TreeViewer.java、LayoutViewer.java来更新视图。

 

读者可以“Step into” loadWindowData方法,可以看到它是通过向ViewServer发送”DUMP”命令来获取整个控件树信息的。

正如我们在《功能实现演示》中讲到的,ViewServer返回给我们的控件树信息是一个内容巨大的文本,HierarchyViewer怎么把这个文本解析成ViewNode树的,而TreeViewer.java,LayoutViewer.java等视图又是如何根据ViewNode来进行绘制的,我们将是下文《前台代码》中讲解。

 

我们到哪了?

现在,我们获取到了该Activity的控件树,并且各个Views – TreeViewer.java、LayoutViewer.java等根据ViewNode树完成了绘制:

20120729215029821

 

加载控件截图

这时,当用户选中hierarchy view(TreeView.java)上的某个节点时,HierarchyViewer将向ViewServer请求该控件的截图,并显示在该节点上面的气泡中,这是怎么做到的呢?

当点击hierarchy view上的节点时,TreeView.java上的selectionChanged方法(override ITreeChangeListener接口)被触发(该事件的触发过程可能要到下文<前台代码>中才能说清楚), 它将调用HierarchyViewerDirector.java的loadCaptureInBackground方法:

    public void loadCaptureInBackground(final ViewNode viewNode) {executeInBackground("Capturing node", new Runnable() {public void run() {loadCapture(viewNode);}});}

 

让我们“Step into” loadCapture方法:

    public Image loadCapture(ViewNode viewNode) {final Image image = DeviceBridge.loadCapture(viewNode.window, viewNode);if (image != null) {viewNode.image = image;// Force the layout viewer to redraw.TreeViewModel.getModel().notifySelectionChanged();}return image;}

 

DeviceBridge.loadCapture(viewNode.window, viewNode) -- DeviceConnection.java向ViewServer发送"CAPTURE”命令来获取控件截图

viewNode.image = image --把截图保存在viewNode中,下次再次选中节点时,就不用再向ViewServer请求了

TreeViewModel.getModel().notifySelectionChanged() -- 强制TreeViewModel向监听者发送SelectionChanged事件。

 

我们到哪了?

获取到控件截图后,TreeViewModel通知hierarchy view进行更新,于是我们看到截图在气泡中显示出来:

201207292150324989

 

总结语

我们试图理清HierarchyViewer后台代码的主要脉络,同时我们似乎也“遗漏”了更多内容:我们没有阅读DeviceBridge.java看它都支持哪些ViewServer命令 -- 我们已经知道的有LIST、DUMP、CAPTURE;我们没有深入阅读AndroidDebugBridge.java是如何工作的(也许不久后我就会写这方面的文章);我们也没有阅读当设备断开、改变时,当进行刷新等操作时的代码。 我想我不能剥夺大家自己去阅读代码的乐趣。

 

本系列的最后一篇,我们将阅读HierarchyViewer的前台代码。

 

本文由知平软件的刘斌华原创,转载请注明出处。

知平软件致力于移动平台自动化测试技术的研究,我们希望通过向社区贡献知识和开源项目,来促进行业和自身的发展。

转载于:https://www.cnblogs.com/vowei/archive/2012/08/08/2627614.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/361294.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

阿里巴巴使命、愿景、价值观、绩效管理中的六大价值观、

阿里巴巴的使命 让天下没有难做的生意 阿里巴巴的愿景 分享数据的第一平台幸福指数最高的企业“活102年”阿里巴巴的价值观 我们坚持“客户第一、员工第二、股东第三”。 阿里巴巴的六脉神剑&#xff08;绩效管理中的六大价值观&#xff09; 公司的“六脉神剑” 客户第一&#…

在命令行上操作JAR,WAR和EAR

尽管Java IDE和许多图形工具使查看和操作Java归档文件&#xff08;JAR&#xff0c;WAR和EAR&#xff09;文件的内容比以往更加容易&#xff0c;但有时我还是更喜欢使用命令行jar命令来完成这些任务。 当我必须重复做某事或作为脚本的一部分来做时&#xff0c;尤其如此。 在本文…

C#语言使用多态(接口与override) ——帮您剔除对面向对象多态性的疑惑

多态是面向对象编程中三大机制之一,其原理建立在"从父类继承而来的子类可以转换为其父类"这个规则之上,换句话说,能用父类的地方,就能用该类的子类.当从父类派生了很多子类时,由于每个子类都有其不同的代码实现,所以当用父类来引用这些子类时,同样的操作而可以表现出…

ASP.NET MVC的生命周期与网址路由

网址路由&#xff08;Routing&#xff09;在ASP.NET MVC中有两个主要用途&#xff0c;一个用途是匹配通过浏览器传来的HTTP请求&#xff0c;另一个用途则是将适当的网址返回浏览器。 首先我们来看下第一个用途&#xff0c;也就是匹配通过浏览器传来的HTTP请求。 客户端对ASP.NE…

Android中SQLiteDatabase操作【附源码】

像我们做的很多应用程序及网站一样&#xff0c;基本都是对数据库进行增删改查来实现相应的功能。那么Android开发也一样&#xff0c;不过由于在移动客户端应用&#xff0c;所以不会像sql server、mysql那么复杂&#xff0c;Android应用程序支持本地数据库&#xff0c;SQLiteDat…

【BEV感知算法概述——下一代自动驾驶感知算法】

文章目录 BEV感知算法概念BEV感知算法数据集介绍BEV感知算法分类BEV感知算法的优劣小结 BEV感知算法概念 Bird’s-Eye-View&#xff0c;鸟瞰图&#xff08;俯视图&#xff09;。BEV感知算法存在许多的优势。 首先&#xff0c;BEV视图存在遮挡小的优点&#xff0c;由于视觉的透…

博客园CodingLife模板样式优化

博客园CodingLife模板样式优化&#xff0c;小屏和大屏均做了优化&#xff0c;感兴趣的园友可以复制到你的页面定制CSS代码中&#xff0c; 源码地址&#xff1a;http://www.cnblogs.com/blog/customcss/334547.css 更多专业前端知识&#xff0c;请上 【猿2048】www.mk2048.com

WPF控件和布局

WPF控件和布局&#xff0c;根据刘铁猛《深入浅出WPF》书籍讲解内容&#xff0c;主要记录控件和布局的原理&#xff0c;如果有不足的地方&#xff0c;请大牛们键盘下留情--轻喷&#xff01;如果还算有用&#xff0c;请给点动力&#xff0c;支持一把&#xff01; 一、WPF里的控件…

Oracle ——概述 Oracle 5 步调优方法论

http://www.toadworld.com/KNOWLEDGE/KnowledgeXpertforOracle/tabid/648/TopicID/OPS3/Default.aspx 对 Oracle 调优应该采取积极的态度。如果等到用户开始抱怨性能&#xff0c;才调优通常以为时已晚&#xff0c;即便是最有效的调优策略。性能问题确定和处理的时间越晚&#x…

整体服务器与微服务

介绍 刚开始时&#xff0c;由于要求简单&#xff0c;所以应用程序既简单又小。 随着时间要求和需求的增长&#xff0c;我们的应用程序变得越来越大&#xff0c;越来越复杂。 这导致将单片服务器开发和部署为一个单元。 在某种程度上&#xff0c;微服务可以通过简单的应用程序回…

NEXUS S安卓4.0/4.1 【完美】 ROOT教程

原文链接&#xff1a;http://bbs.gfan.com/android-3517082-1-1.html 进行bootloader解锁&#xff08;即使解锁&#xff0c;再上锁&#xff09;&#xff0c;会清除你手机上的【所有】数据&#xff08;包括内部的16G SD 存储&#xff09;&#xff0c;包括但不限于应用、设置、联…

HDU 2647 Reward 拓扑排序

http://acm.hdu.edu.cn/showproblem.php?pid2647 题意&#xff1a; 输入N和M代表N个人和M组数据&#xff0c;M组数据中的A和B代表A的工资要比B的工资高&#xff0c;底薪是&#xff08;888元&#xff09;&#xff0c;问你这个老板至少要付 多少钱给这些员工&#xff0c;A比B工资…

提高Java的锁性能

Plumbr是唯一通过解释应用程序性能数据来自动检测Java性能问题的根本原因的解决方案。 几个月前&#xff0c;我们在Plumbr中引入了锁定线程检测之后&#xff0c;我们开始收到类似于“嘿&#xff0c;太好了&#xff0c;现在我知道是什么导致了性能问题&#xff0c;但是现在应该…

[置顶] UDP协议---心德(1)

UDP协议1.面向无连接 2.速度快 3.不可靠的协议&#xff0c;容易丢包 4.包小于64k DatagramSocket:此类表示用来发送和接收数据报包的套接字 DatagramPacket:数据包 发送端&#xff1a; 1.创建一个udpsocket服务 DatagrameSocket dsnew DatagramSocket(); 2.将数据封装到数据包…

非捕获Lambda的实例

大约一个月前&#xff0c;我在Java 8的lambda表达式框架下总结了Brian Goetz的观点 。 目前&#xff0c;我正在研究有关默认方法的文章&#xff0c;令我惊讶的是&#xff0c;我又回到了Java处理lambda表达式的方式。 这两个功能的交集可能会产生微妙但令人惊讶的效果&#xff0…

SQL Server 查询性能优化——创建索引原则(一)

索引是什么&#xff1f;索引是提高查询性能的一个重要工具&#xff0c;索引就是把查询语句所需要的少量数据添加到索引分页中&#xff0c;这样访问数据时只要访问少数索引的分页就可以。但是索引对于提高查询性能也不是万能的&#xff0c;也不是建立越多的索引就越好。索引建少…

WordPress强制跳转https教程

在互联网火热的今天&#xff0c;安全问题显得越来越重要&#xff0c;为了用户信息安全&#xff0c;很多热门网站都启用了https 有小伙伴就问&#xff1a;我启用了https&#xff0c;为什么访问的时候显示的还是http呢&#xff1f; 其实&#xff0c;有时候并不是因为我们ssl证书…

Java 8的装饰器模式

在最近的一篇文章中&#xff0c;我描述了装饰器模式如何拯救了我的一天。 我给出了一个小代码段&#xff0c;其中包含创建装饰器的最简单方法&#xff0c;但承诺Java 8会有更好的方法。 这里是&#xff1a; 用Java 8装饰 HyperlinkListener listener this::changeHtmlViewBa…

WPF中使用流文档灵活地显示内容

WPF中使用流文档灵活地显示内容 by: Markus Egger form: http://msdn.microsoft.com/msdnmag/issues/07/08/wpf/default.aspx?loczh Windows Presentation Foundation (WPF) 提供了一系列功能。事实上&#xff0c;功能…

canvas图表(4) - 散点图

原文地址&#xff1a;canvas图表(4) - 散点图 今天开始完成散点图&#xff0c;做完这一节&#xff0c;我的canvas图表系列就算是完成了&#xff0c;毕竟平时最频繁用到的就是这几类图表了&#xff1a;柱状&#xff0c;折线&#xff0c;饼图&#xff0c;散点。经过编写canvas图表…