第14章4节《MonkeyRunner源码剖析》 HierarchyViewer实现原理-装备ViewServer-端口转发

在初始化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行:设置本地端口到目标机器端ViewServer监听端口的端口转发
  • 41-54行:确定ViewServer线程是否已经启动,没有的话就启动它。
  • 55行:获取ViewServer的版本以及其支持的协议版本

本小节我们先描述第一点,看HierarchyViewer是如何设置本地端口到目标机器端ViewServer监听端口的端口转发的。在第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      * 

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

这个处理端口转发的方法主要分3步走:

  • 第1步:获得本地ViewServer转发端口号
  • 第2步:通过Device类发送adb命令创建本地到ViewServer端口转发
  • 第3步:把本地端口号和对应的设备序列号保存起来以便查找

我们先看第1步,就是121行,这里要注意”sNextLocalPort”这个变量,其实它是个静态变量:

  private static int sNextLocalPort = 4939;

代码14-4-3 DeviceBridge - sNextLocalPort

所以代码14-4-2中121行所代表的意思是:

  • 第一个建立的ViewServer端口转发的本地端口是4939
  • 下一个建立的ViewServer端口转发的本地端口是在4939的基础自增加1

注意这里自增加的写法是”sNextLocalPort ++”,如果反过来写成”++sNextLocalPort”, 那么第一个本地端口就会变成4940了,这些都是Java的基本语法了,这里以防我们做测试的没有太多编程经验,所以指出来。

好我们继续分析第2步端口转发相应代码, 这个方法传入的参数就是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方法来设置端口转发:

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命令行客户端命令的:

“adb -s HT21ATD05099 forward tcp:4939 tcp:4939”

这其实跟第13章第2小节手动发送ViewServer端口转发命令是一样的,只是这里多了个-s参数来指定要转发的端口属于哪个设备上的ViewServer而已。

到现在为止我们已经完成了端口转发的第2步了,那么我们往下看第3步,做的事情就是把代表目标设备的Device实例和本地ViewServer的转发端口做为键值对给保存起来到sDevicePortMap这个成员变量里面:

sDevicePortMap.put(device, Integer.valueOf(localPort));
sDevicePortMap这个成员变量是个HashMap:
55	private static final HashMap sDevicePortMap = new HashMap();
代码14-4-5 DeviceBridge - sDevicePortmap

注意这个变量是很重要的,因为HierarchyViewer连接对应的设备的socket就是靠它来提供对应的本地ViewServer转发端口号的。

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


你可能感兴趣的:(-,书籍)