HierarchyViewer is a very useful tool in the Android SDK package. You can find it in the android-sdks/tools directory. Through HierarchyViewer, even if there is no source code for the application, we can intuitively browse the hierarchical structure of controls in the Activity, as well as the attributes and, this is very helpful for testers to compile automated test cases. In this series of articles, we will read and parse the HierarchyViewer code to learn how HierarchyViewer works, and to learn more about the interfaces provided by Android to developers. The code of this series of articles is based on the source code of android4.0. If you haven't downloaded the source code, download it. The journey begins.
The first step of this article is not to start from the source code reading, but to demo and explain the main working principles of HierarchyViewer. This is the essence of the source code extracted by the author :). After reading this article, you can write your own simple HierarchyViewer. We will mainly explain the following parts:
1. How to connect to ViewServer
2. How to obtain activity Activities
3. How to obtain the control tree of an Activity
4. How to obtain
How to connect to ViewServer
ViewServer is a service provided by Android through port 4939. HierarchyViewer mainly obtains Activity information through it. HierarchyViewer connects to ViewServer through the following three things. This requires Adb. In HierarchyViewer, Adb is called directly through the api. Here we first use the command line adb to implement the same function.
(1) Forword port. It is to map port 4939 on the Android device to a port on the PC. In this way, packets sent to the port on the PC will be forwarded to port 4939 on the Android device.
First, enter the command to list all Android devices.
1 adb devices
Suppose we have multiple devices connected to the PC, and the output of this command is:
1 List of devices attached
2 emulator-5554 device
3 emulator-5556 device
Take the device emulator-5556 as an example. Next we map its port 4939 to port 4939 of the PC:
1 adb-s emulator-5556 forward tcp: 4939 tcp: 4939
If multiple Android devices are connected, HierarchyViewer maps port 4939 of the next Android device to port 4940 of the PC, and so on.
(2) Open the ViewServer service.
First, you need to determine whether the ViewServer is Enabled:
1 adb-s emulator-5556 shell service call window 3
If the returned value is "Result: Parcel (00000000 00000000 '...')", it indicates that ViewServer is not enabled. Use the following command to open ViewServer:
1 adb-s emulator-5556 shell service call window 1 i32 4939
The command to disable ViewServer is:
1 adb-s emulator-5556 shell service call window 2 i32 4939
(3) connect to ViewServer. Since ViewServer has been enabled, we need to connect to it next. Because we have mapped the emulator-5556 port to port 4939 of the PC, we need to connect 127.0.0.1: 4939. You need to write some java code:
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 and in are used to send commands and receive returned data. Note that the communication between HierarchyViewer and ViewServer uses short connections. Therefore, each command sent requires a new connection, therefore, the above code needs to be called repeatedly.
How to obtain Activity
When HierarchyViewer is enabled, the list of active activities of each device is displayed, for example:
How is this implemented? You need to send the "LIST" command to ViewerServer and check the following code:
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.". inclusignorecase (line) {// $ NON-NLS-1 $
11 break;
12}
13 context + = line + "\ r \ n ";
14}
We can obtain a list similar to the following:
01 44fd1b78 com. android. internal. service. wallpaper. ImageWallpaper
02 %7aa28 com. android. launcher/com. android. launcher2.Launcher
03 45047328 com. tencent. mobileqq/com. tencent. mobileqq. activity. HomeActivity
04 bytes b8d18 com. tencent. mobileqq/com. tencent. mobileqq. activity. icationicationactivity
05 451049c0 com. tencent. mobileqq/com. tencent. mobileqq. activity. icationicationactivity
06 451167a8 com. tencent. mobileqq/com. tencent. mobileqq. activity. UpgradeActivity
07 unzip efef0 com. tencent. mobileqq/com. tencent. mobileqq. activity. UpgradeActivity
08 listen 2f2e0 TrackingView
09 1273f560 StatusBarExpanded
10 44fe0bb0 StatusBar
11 44f09250 Keyguard
Note that the hexadecimal number in front of each row is a hashcode. We need to use this hashcode when further requesting the control tree corresponding to the Activity.
How to obtain the control tree of an Activity
After an Activity is selected, HierarchyViewer obtains its control and displays it as a hierarchy chart:
The command for obtaining control tree information is DUMP, followed by the corresponding Activity hash code. If ffffffff is used as the parameter, the front-end Activity is used. Take com. android. launcher2.Launcher as an example. Its hash code is ipv7aa28. Check the code:
01 // out. write ("DUMP ffffff ");
02 out. write ("DUMP route 7aa28 ");
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}
The returned control tree is saved in the text context1. Generally, the content of the text is very large. I will not print it all here. Let's just take one of the lines to see it:
1 android. widget. frameLayout @ 44edba90 mForeground = 52, android. graphics. drawable. ninePatchDrawable @ 44edc1e0 records = 5, false records = mMeasureAllChildren = 5, false records = records () = 24, FIG () = 9, SCROLLING enabled () = 4, true isAnimationCacheEnabled () = 4, true isChildrenDrawingOrderEnabled () = 5, false isChildrenDrawnWithCacheEnabled () = 5, false mMinWidth = 3,480 bytes = 3,800 0 mPaddingLeft = 0 mPaddingRight = 0 mPaddingTop = 38 mMinHeight = Weight = mMeasuredHeight = mLeft = Weight = x 0 Weight = x 0 Weight = x 20 Weight = 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 = mViewFlags = Hangzhou getBaseline () = 2,-1 getHeight () = 3,800 layout_bottomMargin = margin = layout_topMargin = layout_height = 12, MATCH_PARENT layout_width = 12, MATCH_PARENT getTag () = 4, null getVisibility () = 7, VISIBLE getWidth () = 3,480 hasFocus () = 5, false isClickable () = 5, false isDrawingCacheEnabled () = 5, false isEnabled () = 4, true isFocusable () = 5, false isFocusableInTouchMode () = 5, false isFocused () = 5, false isHapticFeedbackEnabled () = 4, true isInTouchMode () = 4, true isOpaque () = 5, false isSelected () = 5, false isSoundEffectsEnabled () = 4, true willNotCacheDrawing () = 5, false willNotDraw () = 5, false
Each row in the returned text is a control in the Activity, which contains all the information of the Control. HierarchyViewer parses the information and displays it in the attribute list. Note that each row contains a "control type @ hash code" field at the beginning, such as android. widget. frameLayout @ 44edba90, which is used to obtain the screen of the control.
How does HierarchyViewer parse this text into a hierarchy chart? It turns out that there are several spaces in front of each line. For example, if there are five spaces in front of each line, it indicates that the control is on the sixth layer, the last four spaces indent control is its parent control. In the articles after this series, we will read how HierarchyViewer parses the text and shows the hierarchy chart.
How to obtain
When a control is selected on the hierarchy chart, HierarchyViewer displays the control:
The obtained command is CAPTURE. You need to pass the hashcode of the Activity and the hashcode of the control as the parameter. See the following code:
1 import org. eclipse. swt. graphics. Image;
2 import org. eclipse. swt. widgets. Display;
3
4 out. write ("CAPTURE defaults 7aa28 android. widget. FrameLayout @ 44edba90 ");
5 out. newLine ();
6 out. flush ();
7
8 Image image = new Image (Display. getDefault (), socket. getInputStream ());
So far, I believe that you have a basic understanding of the main implementation mechanisms of HierarchyViewer. Next, we will really start to read the HierarchyViewer code. The content of the following chapters is probably:
Use Eclipse to read and debug HierarchyViewer
Guide to HierarchyViewer background code
HierarchyViewer front-end code reading