1. 程式人生 > >第14章4節《MonkeyRunner源代碼剖析》 HierarchyViewer實現原理-裝備ViewServer-port轉發

第14章4節《MonkeyRunner源代碼剖析》 HierarchyViewer實現原理-裝備ViewServer-port轉發

是把 atd int set rc4 ccf jna inf xbm

在初始化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)勾搭。

本文由天地會珠海分舵原創。轉載請自覺,是否投訴維權看心情。


第14章4節《MonkeyRunner源代碼剖析》 HierarchyViewer實現原理-裝備ViewServer-port轉發