码迷,mamicode.com
首页 > 其他好文 > 详细

第14章7节《MonkeyRunner源码剖析》 HierarchyViewer实现原理-装备ViewServer-获取版本号

时间:2015-12-08 11:39:48      阅读:247      评论:0      收藏:0      [点我收藏+]

标签:

这里获取的版本号有两个,一个是ViewServer自身的版本号,一个是ViewServer当前使用协议的版本号。

我们这里为什么需要获取ViewServer版本以及其协议版本呢?其实主要原因是ViewServer有些功能在老版本上是不支持的,比如HierarchyViewer在列出当前所有Activity窗口的时候,针对获取焦点的窗口会根据不同的ViewServer协议版本而作不同处理,请看源码示例:

316     public static Window[] loadWindows(IHvDevice hvDevice, IDevice device) {
317         ArrayList<Window> windows = new ArrayList<Window>();
318         DeviceConnection connection = null;
319         ViewServerInfo serverInfo = getViewServerInfo(device);
320         try {
321             connection = new DeviceConnection(device);
322             connection.sendCommand("LIST"); //$NON-NLS-1$
323             BufferedReader in = connection.getInputStream();
324             String line;
325             while ((line = in.readLine()) != null) {

...
          Window w = new Window(hvDevice, line.substring(index + 1), id);
          windows.add(w);
       	}
342             // Automatic refreshing of windows was added in protocol version 3.
343             // Before, the user needed to specify explicitly that he wants to
344             // get the focused window, which was done using a special type of
345             // window with hash code -1.
346             if (serverInfo.protocolVersion < 3) {
347                 windows.add(Window.getFocusedWindow(hvDevice));
348             }
    } catch (Exception e) {
     ...
      }
    }
代码14-7-1 协议版本作用示例DeviceBridge-loadWindows

代码先是发送”LIST”命令到ViewServer列出所有的打开的窗口,然后把每个窗口都保存起来。342行起按照源码的注释解析就是说:从协议版本3以后开始加入了窗口自动更新的功能,但是在此之前,如果用户想要获得一个获得焦点的窗口的话,需要通过显式的创建一个特殊的哈希值为-1的Window实例来完成。怎么知道它的哈希值是-1呢?请看Window类的getfocusedWindow方法:

  public static Window getFocusedWindow(IHvDevice device) {
    return new Window(device, "<Focused Window>", -1);
  }
代码14-7-2 Window-getFocusedWindow方法

然后再看其调用的Window的构造函数和对应的传入参数:

  public Window(IHvDevice device, String title, int hashCode)
  {
    this.mHvDevice = device;
    this.mTitle = title;
    this.mHashCode = hashCode;
    this.mClient = null;
  }
代码14-7-3 Window-构造函数

最终创建的就是一个标题是”<Focused Window>”,哈希值是-1的Window实例。

通过以上的示例,主要是想说明,ViewServer的版本会影响到代码的不同处理方式,所以我们还是很有必要去看下这些版本信息是如何获得和保存起来的。

好,那么我们继续分析HierarchyViewer在装备ViewServer时的方法setupViewServer最后一个调用方法DeviceBridge.loadViewServerInfo,这个方法有点长,我们对它分开来分析,先看第1部分:

260     public static ViewServerInfo loadViewServerInfo(IDevice device) {
261         int server = -1;
262         int protocol = -1;
263         DeviceConnection connection = null;
264         try {
265             connection = new DeviceConnection(device);
266             connection.sendCommand("SERVER"); //$NON-NLS-1$
267             String line = connection.getInputStream().readLine();
268             if (line != null) {
269                 server = Integer.parseInt(line);
270             }
271         } catch (Exception e) {
272             Log.e(TAG, "Unable to get view server version from device " + device);
273         } finally {
274             if (connection != null) {
275                 connection.close();
276             }
277         }
    ...
}
代码14-7-4 DeviceBridge - loadViewServerInfo获取ViewServer版本

265行显示的代码的第一个重要的部分是去建立一个DeviceConnection的连接,传入的参数依然是ddmlib的Device类的实例:

 36     public DeviceConnection(IDevice device) throws IOException {
 37         mSocketChannel = SocketChannel.open();
 38         int port = DeviceBridge.getDeviceLocalPort(device);
 39         if (port == -1) {
 40             throw new IOException();
 41         }
 42         mSocketChannel.connect(new InetSocketAddress("127.0.0.1", port)); //$NON-NLS-1$
 43         mSocketChannel.socket().setSoTimeout(40000);
 44     }
代码14-7-5 DeviceConnection - 构造函数

整个代码所做的事情很清晰明了:

  • 创建一个SocketChannel
  • 根据Device实例获得对应的ViewServer本地转发端口号
  • 把SocketChannel连接上本地的ViewServer转发端口

这里值得提一提的倒是如何根据Device实例获得ViewServer本地转发端口号这个事情。大家还记得第4小节我们说端口转发的时候,最终Device实例和对应的本地转发端口号是保存在DeviceBridge的一个名叫sDevicePortMap的静态成员HashMap里面的。所以这里所做的事情就是去到这个HashMap里面以Device实例为键把端口号这个值取出来而已:

155     public static int getDeviceLocalPort(IDevice device) {
156         synchronized (sDevicePortMap) {
157             Integer port = sDevicePortMap.get(device);
158             if (port != null) {
159                 return port;
160             }
161             Log.e(TAG, "Missing forwarded port for " + device.getSerialNumber());
162             return -1;
163         }
164     }
代码14-7-6 DeviceBridge - getDeviceLocalPort

那么现在我们已经获得ViewServer对应本地的转发端口号了,ViewServer也已经在实例化DeviceConnection的时候给连接好了,剩下的就差发命令了。我们继续看下”代码14-7-4 DeviceBridge - loadViewServerInfo获取ViewServer版本”的第2个重要部分:

connection.sendCommand("SERVER");

很明显就是往刚才DeviceConnection建立好的连接到ViewServer的SocketChannel发送”SERVER”这个命令了:

 62     public void sendCommand(String command) throws IOException {
 63         BufferedWriter out = getOutputStream();
 64         out.write(command);
 65         out.newLine();
 66         out.flush();
 67     }
代码14-7-7 DeviceConnection - sendCommand

到此为止我们就分析完如何发送”SERVER”命令到ViewServer以获得其版本号的,完了吗?还没有了,别忘了我们除了要获得ViewServer的版本号外,还需要获得ViewServer当前使用的协议版本了,”代码14-7-4 DeviceBridge - loadViewServerInfo获取ViewServer版本”只是loadViewServerInfo的一部分而已。现在我们往下分析第2部分,但它是怎么去获得协议版本的:

260     public static ViewServerInfo loadViewServerInfo(IDevice device) {
261         int server = -1;
262         int protocol = -1;
    ... //获得ViewServer版本号部分略
278         connection = null;
279         try {
280             connection = new DeviceConnection(device);
281             connection.sendCommand("PROTOCOL"); //$NON-NLS-1$
282             String line = connection.getInputStream().readLine();
283             if (line != null) {
284                 protocol = Integer.parseInt(line);
285             }
286         }
...
  }
代码14-7-8 DeviceBridge - loadViewServerInfo获取协议版本

可以看到,其实流程跟代码14-7-4一模一样,只是最终发送给ViewServer的命令是代表获取协议的”PROTOCOL”而非”SERVER”而已。

最后我们看在获得这些版本号之后loadViewServerInfo是如何处理的:

260     public static ViewServerInfo loadViewServerInfo(IDevice device) {
261         int server = -1;
262         int protocol = -1;
    ...
296         ViewServerInfo returnValue = new ViewServerInfo(server, protocol);
297         synchronized (sViewServerInfo) {
298             sViewServerInfo.put(device, returnValue);
299         }
300         return returnValue;
301     }
代码14-7-9 DeviceBridge - loadViewServerInfo收尾

最后收尾阶段做两个事情:

  • 第一件:根据获得协议创建ViewServerInfo实例
  • 第二件:把这个实例保存起来到sViewServerInfo这个HashMap里面,跟上面提到的sDevicePortMap一样,还是用代表设备的Device类的实例做为键,而值是刚创建的这个ViewServerInfo实例

这里我们先看看ViewServerInfo这个类,其实它是DeviceBridge的内部类:

 59     public static class ViewServerInfo {
 60         public final int protocolVersion;
 61         public final int serverVersion;
 62         ViewServerInfo(int serverVersion, int protocolVersion) {
 63             this.protocolVersion = protocolVersion;
 64             this.serverVersion = serverVersion;
 65         }
 66     }
代码14-7-10 DeviceBridge - ViewServerInfo

里面就两个成员变量一个构造函数完了,两个成员类变量分别保存的是我们刚才获得的ViewServer版本号和ViewServer协议版本号。

注:更多文章请关注公众号:techgogogo或个人博客http://techgogogo.com。当然,也非常欢迎您直接微信(zhubaitian1)勾搭。本文由天地会珠海分舵原创。转载请自觉,是否投诉维权看心情。


第14章7节《MonkeyRunner源码剖析》 HierarchyViewer实现原理-装备ViewServer-获取版本号

标签:

原文地址:http://blog.csdn.net/zhubaitian/article/details/50212915

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!