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

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

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

316public static Window[] loadWindows(IHvDevice hvDevice, IDevice device) {317ArrayList<Window> windows = new ArrayList<Window>();318DeviceConnection connection = null;319ViewServerInfo serverInfo = getViewServerInfo(device);320try {321connection = new DeviceConnection(device);322connection.sendCommand("LIST"); //$NON-NLS-1$323BufferedReader in = connection.getInputStream();324String line;325while ((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 to344// get the focused window, which was done using a special type of345// window with hash code -1.346if (serverInfo.protocolVersion < 3) {347windows.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部分:

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

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

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

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

创建一个SocketChannel

根据Device实例获得对应的ViewServer本地转发端口号

把SocketChannel连接上本地的ViewServer转发端口

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

155public static int getDeviceLocalPort(IDevice device) {156synchronized (sDevicePortMap) {157Integer port = sDevicePortMap.get(device);158if (port != null) {159return port;160}161Log.e(TAG, "Missing forwarded port for " + device.getSerialNumber());162return -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”这个命令了:

62public void sendCommand(String command) throws IOException { 63BufferedWriter out = getOutputStream(); 64out.write(command); 65out.newLine(); 66out.flush(); 67}代码14-7-7 DeviceConnection – sendCommand每天告诉自己一次,『我真的很不错』

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

相关文章:

你感兴趣的文章:

标签云: