在初始化HierarchyViewer的实例过程中,HierarchyViewer会调用自己的成员方法setupViewServer来把ViewServer装备好,那么我们这里先看下这种方法:
39 private void setupViewServer() { 40 DeviceBridge.setupDeviceForward(mDevice); 41 if (!DeviceBridge.isViewServerRunning(mDevice)) { 42 if (!DeviceBridge.startViewServer(mDevice)) { 43 // TODO: Get rid of this delay. 44 try { 45 Thread.sleep(2000); 46 } catch (InterruptedException e) { 47 } 48 if (!DeviceBridge.startViewServer(mDevice)) { 49 Log.e(TAG, "Unable to debug device " + mDevice); 50 throw new RuntimeException("Could not connect to the view server"); 51 } 52 return; 53 } 54 } 55 DeviceBridge.loadViewServerInfo(mDevice); 56 }
代码14-4-1 HierarchyViewer-setupViewServer
从以上代码中我们能够看到该方法去装备ViewServer主要做的事情有例如以下几点:
- 40行:设置本地port到目标机器端ViewServer监听port的port转发
- 41-54行:确定ViewServer线程是否已经启动,没有的话就启动它。
- 55行:获取ViewServer的版本号以及其支持的协议版本号
本小节我们先描写叙述第一点,看HierarchyViewer是怎样设置本地port到目标机器端ViewServer监听port的port转发的。在第13章第2小节我们也手动做过这个事情,当时发送的命令是:
adb forward tcp:4939 tcp:4939
那么HierarchyViewer是不是也是通过代码做同样的事情呢?那么我们带着这个疑问来进入深入的代码分析。我们进入setupDeviceForward这种方法:
110 /** 111 * Sets up a just-connected device to work with the view server. 112 * <p/> 113 * This starts a port forwarding between a local port and a port on the 114 * device. 115 * 116 * @param device 117 */ 118 public static void setupDeviceForward(IDevice device) { 119 synchronized (sDevicePortMap) { 120 if (device.getState() == IDevice.DeviceState.ONLINE) { 121 int localPort = sNextLocalPort++; 122 try { 123 device.createForward(localPort, DEFAULT_SERVER_PORT); 124 sDevicePortMap.put(device, localPort); 125 } catch (TimeoutException e) { 126 Log.e(TAG, "Timeout setting up port forwarding for " + device); 127 } catch (AdbCommandRejectedException e) { 128 Log.e(TAG, String.format("Adb rejected forward command for device %1$s: %2$s", 129 device, e.getMessage())); 130 } catch (IOException e) { 131 Log.e(TAG, String.format("Failed to create forward for device %1$s: %2$s", 132 device, e.getMessage())); 133 } 134 } 135 } 136 }
代码14-4-2 DeviceBridge - setupDeviceForward
这个处理port转发的方法主要分3步走:
- 第1步:获得本地ViewServer转发port号
- 第2步:通过Device类发送adb命令创建本地到ViewServerport转发
- 第3步:把本地port号和相应的设备序列号保存起来以便查找
我们先看第1步,就是121行,这里要注意”sNextLocalPort”这个变量。事实上它是个静态变量:
private static int sNextLocalPort = 4939;
代码14-4-3 DeviceBridge - sNextLocalPort
所以代码14-4-2中121行所代表的意思是:
- 第一个建立的ViewServerport转发的本地port是4939
- 下一个建立的ViewServerport转发的本地port是在4939的基础自添加1
注意这里自添加的写法是”sNextLocalPort ++”。假设反过来写成”++sNextLocalPort”, 那么第一个本地port就会变成4940了。这些都是Java的基本的语法了,这里以防我们做測试的没有太多编程经验,所以指出来。
好我们继续分析第2步port转发相应代码, 这种方法传入的參数就是HierarchyViewer的成员变量mDevice,依据本章第3小节的描写叙述,这个变量是ddmlib中的Device类的一个实例,所以以上调用”device.createForward”方法实际上调用的就是Device的createForward方法:
644 @Override 645 public void createForward(int localPort, int remotePort) 646 throws TimeoutException, AdbCommandRejectedException, IOException { 647 AdbHelper.createForward(AndroidDebugBridge.getSocketAddress(), this, 648 String.format("tcp:%d", localPort), //$NON-NLS-1$ 649 String.format("tcp:%d", remotePort)); //$NON-NLS-1$ 650 }
代码14-4-3 Device - createForward
像第10章《MonkeyDevice实现原理基础》所描写叙述的那样。Device终于直接调用AdbHelper静态类的createForward方法来设置port转发:
549 public static void createForward(InetSocketAddress adbSockAddr, Device device, 550 String localPortSpec, String remotePortSpec) 551 throws TimeoutException, AdbCommandRejectedException, IOException { 552 553 SocketChannel adbChan = null; 554 try { 555 adbChan = SocketChannel.open(adbSockAddr); 556 adbChan.configureBlocking(false); 557 558 byte[] request = formAdbRequest(String.format( 559 "host-serial:%1$s:forward:%2$s;%3$s", //$NON-NLS-1$ 560 device.getSerialNumber(), localPortSpec, remotePortSpec)); 561 562 write(adbChan, request); 563 564 AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */); 565 if (!resp.okay) { 566 Log.w("create-forward", "Error creating forward: " + resp.message); 567 throw new AdbCommandRejectedException(resp.message); 568 } 569 } finally { 570 if (adbChan != null) { 571 adbChan.close(); 572 } 573 } 574 }
代码14-4-4 AdbHelper - createForward
formAdbRequest我们在之前已经分析过。做的事情就是组建好ADB协议的命令以待发送给ADB服务器,在我们558行中终于组建好的ADB协议命令将会例如以下:
“host-serial:xxx:forward:localPortSpec;remotePortSpec”
当中xxx就是代表目标设备的序列号,能够通过”adb devices -l”获得:
图14-4-1获取设备序列号
所以在终于这个ADB协议命令字串将会变成:
“host-serial:HT21ATD05099:foward:4939;4939”
而參照ADB协议,实际上就相当于ADB命令行client命令的:
“adb -s HT21ATD05099 forward tcp:4939 tcp:4939”
这事实上跟第13章第2小节手动发送ViewServerport转发命令是一样的,仅仅是这里多了个-s參数来指定要转发的port属于哪个设备上的ViewServer而已。
到如今为止我们已经完毕了port转发的第2步了,那么我们往下看第3步,做的事情就是把代表目标设备的Device实例和本地ViewServer的转发port做为键值对给保存起来到sDevicePortMap这个成员变量里面:
sDevicePortMap.put(device, Integer.valueOf(localPort)); sDevicePortMap这个成员变量是个HashMap: 55 private static final HashMap<IDevice, Integer> sDevicePortMap = new HashMap();
代码14-4-5 DeviceBridge - sDevicePortmap
注意这个变量是非常重要的,由于HierarchyViewer连接相应的设备的socket就是靠它来提供相应的本地ViewServer转发port号的。
注:很多其它文章请关注公众号:techgogogo或个人博客http://techgogogo.com。
当然,也非常欢迎您直接微信(zhubaitian1)勾搭。
本文由天地会珠海分舵原创。转载请自觉,是否投诉维权看心情。