当前位置:操作系统 > 安卓/Android >>

Android工具HierarchyViewer 代码导读(1) -- 功能实现演示

HierarchyViewer是Android SDK包中一个非常好用的工具,你在 android-sdks/tools目录下可以找到它。通过HierarchyViewer,即使没有应用的源代码,我们也可以非常直观地浏览Activity中控件的层次结构图,以及每个控件的属性和截图,这对于测试人员编写自动化测试用例是极有帮助的。这个系列的文章,我们将通过阅读和解析HierarchyViewer的代码,来了解HierarchyViewer是如何工作的,也可以加深Android提供给开发者的各种接口的了解。本系列文章代码基于android4.0的源代码,还没有下载源代码的同学快去下载吧,旅程这就开始了。

本文首先并不直接从源代码阅读开始,而是demo和解释HierarchyViewer的主要工作原理,这可是作者从源代码中抽取的精华啊:)。看完本文,你就可以写一个自己简单的HierarchyViewer了。我们主要讲解如下几个部分:

1,如何连接ViewServer

2,如何获取活动的Activities

3,如何获取Activity的控件树

4,如何获取截图

 

如何连接ViewServer

ViewServer是Android通过4939端口提供的服务,HierarchyViewer主要是通过它来获取获取Activity信息的, HierarchyViewer主要做下面3件事情来连接ViewServer。这需要用到Adb,HierarchyViewer中是直接通过api来调用Adb的,而这里我们先使用命令行adb来实现同样的功能。

(1)Forword端口。就是把Android设备上的4939端口映射到PC的某端口上,这样,向PC的该端口号发包都会转发到Android设备的4939端口上。

首先,输入命令列出所有Android设备

 1 adb devices

 

假设我们有多台设备连接在PC上,该命令的输出为:

 1 List of devices attached  

2 emulator-5554   device 

3 emulator-5556   device

 

以设备emulator-5556为例,接下来我们把它的4939端口映射到PC的4939端口上:
 1 adb -s emulator-5556 forward tcp:4939 tcp:4939

如果连接了多台Android设备,HierarchyViewer将把下一台Android设备的4939端口映射到PC的4940端口,以此类推。

 

(2)打开ViewServer服务。

首先,需要判断ViewServer是否打开:
 1 adb -s emulator-5556 shell service call window 3

 

如果返回值是"Result: Parcel(00000000 00000000 '........')",说明ViewServer没有打开,那么需要用下面的命令打开ViewServer:
 1 adb -s emulator-5556 shell service call window 1 i32 4939

 

反之,关闭ViewServer的命令是:
 1 adb -s emulator-5556 shell service call window 2 i32 4939

 

(3)连接ViewServer,既然ViewServer已经打开,那么下一步我们就需要连接它了。由于我们已经把设备emulator-5556的4939端口映射为PC的4939端口上,所以我们需要连接的是127.0.0.1:4939。这需要写一些java代码:

 01 import java.net.*; 

02   

03 try{ 

04     Socket socket = new Socket(); 

05     socket.connect(new InetSocketAddress("127.0.0.1", 4939),40000); 

06     BufferedWriter out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); 

07     BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream(), "utf-8")); 

08 }  

09 } catch ( Exception e ) { 

10       e.printStackTrace();  

11 }

out和in用于发送命令和接受返回数据,需要注意的是,HierarchyViewer和ViewServer的通信采用短连接,所以每发送一次命令,需要重新建立一次连接,所以以上代码需要反复调用。

 

如何获取活动的Activity

在打开HierarchyViewer时,会显示每个设备当前活动的Activity列表,如下图:

 

 image

这是怎么实现的呢? 这需要向ViewerServer发送"LIST"命令,看下面的代码:

 01 //send ‘LIST’ command 

02 out.write("LIST"); 

03 out.newLine(); 

04 out.flush(); 

05   

06 //receive response from viewserver 

07 String context=""; 

08 String line; 

09 while ((line = in.readLine()) != null) { 

10             if ("DONE.".equalsIgnoreCase(line)) { //$NON-NLS-1$ 

11                 break; 

12             } 

13             context+=line+"\r\n"; 

14 }

 

我们可以获取到类似如下的列表
 01 44fd1b78 com.android.internal.service.wallpaper.ImageWallpaper 

02 4507aa28 com.android.launcher/com.android.launcher2.Launcher 

03 45047328 com.tencent.mobileqq/com.tencent.mobileqq.activity.HomeActivity 

04 450b8d18 com.tencent.mobileqq/com.tencent.mobileqq.activity.NotificationActivity 

05 451049c0 com.tencent.mobileqq/com.tencent.mobileqq.activity.NotificationActivity 

06 451167a8 com.tencent.mobileqq/com.tencent.mobileqq.activity.UpgradeActivity 

07 450efef0 com.tencent.mobileqq/com.tencent.mobileqq.activity.UpgradeActivity 

08 4502f2e0 TrackingView 

09 4503f560 StatusBarExpanded 

10 44fe0bb0 StatusBar 

11 44f09250 Keyguard

注意,每行前面的16进制数字,那是一个hashcode,我们在进一步请求该Activity对应的控件树时要用到该hashcode。

 

如何获取Activity的控件树
选中一个Activity后,HierarchyViewer将获取它的控件并显示为层次图:

 

 image

获取控件树信息的命令是DUMP,后面要接对应的Activity的hash code,如果使用ffffffff作为参数,那么就是取最前端的Activity。我们以com.android.launcher2.Launcher为例,它的hash code是4507aa28,看代码:

 01 //out.write("DUMP ffffffff"); 

02 out.write("DUMP 4507aa28"); 

03 out.newLine(); 

04 out.flush(); 

05           

06 String context1=""; 

07 line=""; 

08 while ((line = in.readLine()) != null) { 

09     if ("DONE.".equalsIgnoreCase(line)) { //$NON-NLS-1$ 

10         break; 

11     } 

12     context1+=line+"\r\n"; 

13 }

 

返回的控件树被保存文本context1中,一般文本的内容都非常大,这里我不把它全部打印出来,我们只取其中一行来看:

 1 android.widget.FrameLayout@44edba90 mForeground=52,android.graphics.drawable.NinePatchDrawable@44edc1e0 mForegroundInPadding=5,false mForegroundPaddingBottom=1,0 mForegroundPaddingLeft=1,0 mForegroundPaddingRight=1,0 mForegroundPaddingTop=1,0 mMeasureAllChildren=5,false mForegroundGravity=2,55 getDescendantFocusability()=24,FOCUS_BEFORE_DESCENDANTS getPersistentDrawingCache()=9,SCROLLING isAlwaysDrawnWithCacheEnabled()=4,true isAnimationCacheEnabled()=4,true isChildrenDrawingOrderEnabled()=5,false isChildrenDrawnWithCacheEnabled()=5,false mMinWidth=1,0 mPaddingBottom=1,0 mPaddingLeft=1,0 mPaddingRight=1,0 mPaddingTop=2,38 mMinHeight=1,0 mMeasuredWidth=3,480 mMeasuredHeight=3,800 mLeft=1,0 mPrivateFlags_DRAWING_CACHE_INVALID=3,0x0 mPrivateFlags_DRAWN=4,0x20 mPrivateFlags=8,16911408 mID=10,id/content mRight=3,480 mScrollX=1,0 mScrollY=1,0 mTop=1,0 mBottom=3,800 mUserPaddingBottom=1,0 mUserPaddingRight=1,0 mViewFlags=9,402653186 getBaseline(

补充:移动开发 , Android ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,