真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

MonkeyRunner源碼分析之與Android設(shè)備通訊方式

如前文《誰動了我的截圖?--Monkeyrunner takeSnapshot方法源碼跟蹤分析》所述,本文主要會嘗試描述android的自動化測試框架MonkeyRunner究竟是如何和目標設(shè)備進行通信的。

成都創(chuàng)新互聯(lián)公司成立與2013年,先為祁陽等服務(wù)建站,祁陽等地企業(yè),進行企業(yè)商務(wù)咨詢服務(wù)。為祁陽企業(yè)網(wǎng)站制作PC+手機+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問題。

在上一篇文章中我們其實已經(jīng)描述了其中一個方法,就是通過adb協(xié)議發(fā)送adb服務(wù)器請求的方式驅(qū)動android設(shè)備的adbd守護進程去獲取FrameBuffer的數(shù)據(jù)生成屏幕截圖。那么MonkeyRunner還會用其他方式和目標設(shè)備進行通信嗎?答案是肯定的,且看我們一步步分析道來。


1.概述

MonkeyRunner和目標設(shè)備打交道都是通過ChimpChat層進行封裝分發(fā)但最終是在ddmlib進行處理的,其中囊括的方法大體如下:

  • 發(fā)送monkey命令:MonkeyRunner先通過adb shell發(fā)送命令"monkey -port  12345"在目標機器上啟動monkey以監(jiān)聽端口接受連接,然后MonkeyRunner通過連接該端口建立socket并發(fā)送monkey命令。所有與界面相關(guān)的操作都是通過這種方式發(fā)送到目標機器的。
  • 發(fā)送adb協(xié)議請求:通過發(fā)送adb協(xié)議請求來與目標設(shè)備通信的,詳情請查看<<誰動了我的截圖?--Monkeyrunner takeSnapshot方法源碼跟蹤分析>>和<>,其實adb命令行客戶端的所有命令最終都是通過發(fā)送遵循adb協(xié)議的請求來實現(xiàn)的,只是做成命令行方式方便終端用戶使用而已
  • 發(fā)送adb shell命令:模擬adb命令行工具發(fā)送adb shell命令,只是不是真正的直接命令行調(diào)用adb工具,而是在每一個命令執(zhí)行之前先通過上面的“發(fā)送adb協(xié)議請求“發(fā)送“shell:”請求建立一個和adb服務(wù)器通信的adb shell的socket連接通道,adb服務(wù)器再和目標設(shè)備的adb守護進程進行通信

以下是MonkeyDevice所有請求對應(yīng)的與設(shè)備通信方式

請求

是否需要和目標設(shè)備通信

通信方式

注解

發(fā)送adb shell命令

getSystemProperty

發(fā)送adb shell命令


installPackage

發(fā)送adb shell命令

傳送數(shù)據(jù)時發(fā)送adb協(xié)議請求,發(fā)送安裝命令時使用adb shell命令

startActivity

發(fā)送adb shell命令


broadcastIntent

發(fā)送adb shell命令


instrument

發(fā)送adb shell命令


shell

發(fā)送adb shell命令

命令為空,所以相當于直接執(zhí)行”adb shell “

removePackage

發(fā)送adb shell命令


發(fā)送monkey命令

getProperty

發(fā)送monkey命令  


wake

發(fā)送monkey命令  


dispose 

發(fā)送monkey命令   


press

發(fā)送monkey命令  


type

發(fā)送monkey命令  


touch

發(fā)送monkey命令  


drag

發(fā)送monkey命令  


getViewIdList

發(fā)送monkey命令  


getView

發(fā)送monkey命令  


getViews

發(fā)送monkey命令  


getRootView

發(fā)送monkey命令  


發(fā)送adb協(xié)議請求

takeSnapshot

發(fā)送adb協(xié)議請求


reboot

發(fā)送adb協(xié)議命令


installPackage

發(fā)送adb協(xié)議請求

相當于直接發(fā)送adb命令行命令’adb push’

分析之前請大家準備好對應(yīng)的幾個庫的源碼:

MonkeyRunner源碼分析之與Android設(shè)備通訊方式

2. 發(fā)送monkey命令

在剖析如何發(fā)送monkey命令之前,我們需要先去了解一個類,因為這個類是處理所有monkey命令的關(guān)鍵,這就是ChimpChat庫的ChimpManager類。
我們先查看其構(gòu)造函數(shù),看它是怎么初始化的:

/*     */   private Socket monkeySocket; /*     */    /*     */   private BufferedWriter monkeyWriter; /*     */    /*     */   private BufferedReader monkeyReader; /*     */    /*     */  /*     */   public ChimpManager(Socket monkeySocket) /*     */     throws IOException /*     */   { /*  62 */     this.monkeySocket = monkeySocket; /*  63 */     this.monkeyWriter = new BufferedWriter(new OutputStreamWriter(monkeySocket.getOutputStream())); /*     */      /*  65 */     this.monkeyReader = new BufferedReader(new InputStreamReader(monkeySocket.getInputStream())); /*     */   }
初始化所做的事情如下

  • 把構(gòu)造函數(shù)傳進來的monkeySocket這個socket對象保存起來,往下會分析這個socket是如何創(chuàng)立的
  • 初始化monkeyWriter這個BufferedWriter,今后往monkey的socket發(fā)送命令的時候用的就是它
  • 初始化monkeyReader這個BufferedReader,今后從monkey的socket讀返回的時候用的就是它
好,那么現(xiàn)在我們返回來看這個類是什么時候?qū)嵗?。請定位到AdbChimpDevice的構(gòu)造函數(shù):

/*     */   private ChimpManager manager; /*     */    /*     */   public AdbChimpDevice(IDevice device) /*     */   { /*  70 */     this.device = device; /*  71 */     this.manager = createManager("127.0.0.1", 12345); /*     */      /*  73 */     Preconditions.checkNotNull(this.manager); /*     */   }
可以看到ChimpManager是在AdbChimpDevice構(gòu)造的時候已經(jīng)開始初始化的了,初始化傳入的地址是"127.0.0.1"和端口是12345,這個是在下面分析的createManager這個方法中創(chuàng)建socket用的,也就是我們上面提到的monkeySocket.在繼續(xù)之前這里我們先整理下思路,結(jié)合上一篇文章,我們看到幾個重要的類的初始化流程是這樣的:
  • MonkeyRunner在啟動的時候會先啟動MonkeyRunnerStarter這個類,該類的構(gòu)造函數(shù)調(diào)用ChimpChat的getInstance方法實例化ChimpChat.
  • ChimpChat的getInstance方法會先實例化AdbBackend這個類,然后構(gòu)建 ChimpChat自身這個實例
  • 用戶調(diào)用MonkeyRunner.waitForConnection()方法初始化MonkeyDevice
  • 以上的waitForConnection()又調(diào)用的是ChimpChat的waitforConnection()方法
  • ChimpChat的waitForConnection方法調(diào)用的是AdbBackend的waitForConnection方法最終會findAttachedDevice找到目標設(shè)備然后用該設(shè)備初始化AdbChimpDevice
根據(jù)以上的流程我們就很清晰AdbChimpDevice其實在測試腳本一調(diào)用MonkeyRunner.waitForConnection方法的時候就已經(jīng)會初始化的了,也就是說ChimpManager也在這個時候已經(jīng)初始化的了。

好,那么我們繼續(xù)看AdbChimpDevice里面的方法createManager是如何對ChimpManager進行初始化的:

/*     */   private ChimpManager createManager(String address, int port) { /*     */     try { /* 125 */       this.device.createForward(port, port); /*     */     } catch (TimeoutException e) { /* 127 */       LOG.log(Level.SEVERE, "Timeout creating adb port forwarding", e); /* 128 */       return null; /*     */     } catch (AdbCommandRejectedException e) { /* 130 */       LOG.log(Level.SEVERE, "Adb rejected adb port forwarding command: " + e.getMessage(), e); /* 131 */       return null; /*     */     } catch (IOException e) { /* 133 */       LOG.log(Level.SEVERE, "Unable to create adb port forwarding: " + e.getMessage(), e); /* 134 */       return null; /*     */     } /*     */      /* 137 */     String command = "monkey --port " + port; /* 138 */     executeAsyncCommand(command, new LoggingOutputReceiver(LOG, Level.FINE)); /*     */      /*     */     try /*     */     { /* 142 */       Thread.sleep(1000L); /*     */     } catch (InterruptedException e) { /* 144 */       LOG.log(Level.SEVERE, "Unable to sleep", e); /*     */     } /*     */     InetAddress addr; /*     */     try /*     */     { /* 149 */       addr = InetAddress.getByName(address); /*     */     } catch (UnknownHostException e) { /* 151 */       LOG.log(Level.SEVERE, "Unable to convert address into InetAddress: " + address, e); /* 152 */       return null; /*     */     } /*     */      /*     */  /*     */  /*     */  /*     */  /* 159 */     boolean success = false; /* 160 */     ChimpManager mm = null; /* 161 */     long start = System.currentTimeMillis(); /*     */      /* 163 */     while (!success) { /* 164 */       long now = System.currentTimeMillis(); /* 165 */       long diff = now - start; /* 166 */       if (diff > 30000L) { /* 167 */         LOG.severe("Timeout while trying to create chimp mananger"); /* 168 */         return null; /*     */       } /*     */       try /*     */       { /* 172 */         Thread.sleep(1000L); /*     */       } catch (InterruptedException e) { /* 174 */         LOG.log(Level.SEVERE, "Unable to sleep", e); /*     */       } /*     */       Socket monkeySocket; /*     */       try /*     */       { /* 179 */         monkeySocket = new Socket(addr, port); /*     */       } catch (IOException e) { /* 181 */         LOG.log(Level.FINE, "Unable to connect socket", e); /* 182 */         success = false; } /* 183 */       continue; /*     */        /*     */       try /*     */       { /* 187 */         mm = new ChimpManager(monkeySocket); /*     */       } catch (IOException e) { /* 189 */         LOG.log(Level.SEVERE, "Unable to open writer and reader to socket"); } /* 190 */       continue; /*     */        /*     */       try /*     */       { /* 194 */         mm.wake(); /*     */       } catch (IOException e) { /* 196 */         LOG.log(Level.FINE, "Unable to wake up device", e); /* 197 */         success = false; } /* 198 */       continue; /*     */        /* 200 */       success = true; /*     */     } /*     */      /* 203 */     return mm; /*     */   }
這個方法比較長,但大體做的事情如下:

  • 通過調(diào)用ddmlib的device類里面的createForward方法來把主機pc端本地的端口轉(zhuǎn)發(fā)給目標機器端的monkey監(jiān)聽端口,這樣子做的好處是我們通過直接連接主機pc端的轉(zhuǎn)發(fā)端口發(fā)送命令就會等同于通過網(wǎng)絡(luò)連接上目標機器的monkey監(jiān)聽端口來發(fā)送monkey命令
  • 調(diào)用executeAsyncCommand方法發(fā)送異步adb shell命令 “monkey -port"到目標機器開啟monkey并監(jiān)聽以上描述的端口
  • 創(chuàng)建連接到主機pc對應(yīng)目標設(shè)備monkey監(jiān)聽端口的monkeySocket
  • 把該monkeySocket傳遞到本章節(jié)開頭說的ChimpManager構(gòu)造函數(shù)對ChimpManager進行實例化
分析到這里我們可以看到monkey已經(jīng)在目標機器起來了,那么我們就需要去分析MonkeyRunner是如何發(fā)送monkey命令過去控制設(shè)備的了。這里我們會以典型的press這個方法作為例子來進行闡述。
我們先看AdbChimpDevice里面的press方法:
/*     */   public void press(String keyName, TouchPressType type) /*     */   { /*     */     try /*     */     { /* 326 */       switch (3.$SwitchMap$com$android$chimpchat$core$TouchPressType[type.ordinal()]) { /*     */       case 1:  /* 328 */         this.manager.press(keyName); /* 329 */         break; /*     */       case 2:  /* 331 */         this.manager.keyDown(keyName); /* 332 */         break; /*     */       case 3:  /* 334 */         this.manager.keyUp(keyName); /*     */       } /*     */     } /*     */     catch (IOException e) { /* 338 */       LOG.log(Level.SEVERE, "Error sending press event: " + keyName + " " + type, e); /*     */     } /*     */   }
方法很簡單,就是根據(jù)不同的按下類型來調(diào)用ChimpManager中不同的press的方法,我們這里假設(shè)用戶按下的是 DOWN_AND_UP這個類型,也就是說調(diào)用的是ChimpMananer里面的press方法:
/*     */   public boolean press(String name) /*     */     throws IOException /*     */   { /* 135 */     return sendMonkeyEvent("press " + name); /*     */   }
跟著調(diào)用sendMonkeyEvent:
/*     */   private boolean sendMonkeyEvent(String command) /*     */     throws IOException /*     */   { /* 234 */     synchronized (this) { /* 235 */       String monkeyResponse = sendMonkeyEventAndGetResponse(command); /* 236 */       return parseResponseForSuccess(monkeyResponse); /*     */     } /*     */   }
跟著調(diào)用sendMonkeyEventAndGetResponse方法:
/*     */   private String sendMonkeyEventAndGetResponse(String command) /*     */     throws IOException /*     */   { /* 182 */     command = command.trim(); /* 183 */     LOG.info("Monkey Command: " + command + "."); /*     */      /*     */  /* 186 */     this.monkeyWriter.write(command + "\n"); /* 187 */     this.monkeyWriter.flush(); /* 188 */     return this.monkeyReader.readLine(); /*     */   }
以上這幾個方法都是在ChimpManager這個類里面的成員方法。從最后這個sendMonkeyEventAndGetResponse方法我們可以看到它所做的事情就是用我們前面描述的monkeyWritter和monkeyReader這兩個成員變量往主機pc這邊的終會轉(zhuǎn)發(fā)給目標機器monkey那個端口(其實就是上面的monkeySocket)進行讀寫操作。


3. 發(fā)送adb協(xié)議請求

請查看《誰動了我的截圖?--Monkeyrunner takeSnapshot方法源碼跟蹤分析》

4. 發(fā)送adb shell命令


通過上一篇文章《誰動了我的截圖?--Monkeyrunner takeSnapshot方法源碼跟蹤分析》的分析,我們知道MonkeyRunner分發(fā)不同的設(shè)備控制信息是在ChimpChat庫的AdbChimpDevice這個類里面進行的。所以這里我就不會從頭開始分析我們是怎么進入到這個類里面的了,大家不清楚的請先查看上一篇投石問路的文章再返回來看本文。

這里我們嘗試以getSystemProperty這個稍微復(fù)雜點的方法為例子分析下MonkeyRunner是真么通過adb shell發(fā)送命令的,我們首先定位到AdbChimpDevice的該方法:

/*     */   public String getSystemProperty(String key) /*     */   { /* 224 */     return this.device.getProperty(key); /*     */   }

這里的device成員函數(shù)指的就是ddmlib庫里面的Device這個類(請查看上一篇文章),那么我們進去該類看下getProperty這個方法:

/*      */   public String getProperty(String name) /*      */   { /*  379 */     return (String)this.mProperties.get(name); /*      */   }
該方法直接使用mProperties這個Device類的成員變量的get方法根據(jù)property的名字獲得返回值,從定義可以看出這是個map:

/*   65 */   private final Map mProperties = new HashMap();
且這個map是在初始化Device實例之前就已經(jīng)定義好的了,因為其構(gòu)造函數(shù)并沒有代碼提及,但是我們可以看到Device類里面有一個函數(shù)專門往這個map里面添加property:

/*      */   void addProperty(String label, String value) { /*  779 */     this.mProperties.put(label, value); /*      */   }
那么這個addProperty又是在哪里被調(diào)用了呢?一番查看后發(fā)現(xiàn)是在ddmlib里面的GetPropertyReceiver這個類里面的processNewLines這個方法:

/*    */   public void processNewLines(String[] lines) /*    */   { /* 49 */     for (String line : lines) { /* 50 */       if ((!line.isEmpty()) && (!line.startsWith("#"))) /*    */       { /*    */  /*    */  /* 54 */         Matcher m = GETPROP_PATTERN.matcher(line); /* 55 */         if (m.matches()) { /* 56 */           String label = m.group(1); /* 57 */           String value = m.group(2); /*    */            /* 59 */           if (!label.isEmpty()) { /* 60 */             this.mDevice.addProperty(label, value); /*    */           } /*    */         } /*    */       } /*    */     } /*    */   }
給這個map增加所有property的地方是知道了,但是問題是什么時候增加呢?這里我們先賣個關(guān)子。

繼續(xù)之前我們先要了解下ddmlib這個庫里面的DeviceMonitor這個類,這個類會啟動一個線程來監(jiān)控所有連接到主機的設(shè)備的狀態(tài)。

/*      */   boolean start() /*      */   { /*  715 */     if ((this.mAdbOsLocation != null) && (sAdbServerPort != 0) && ((!this.mVersionCheck) || (!startAdb()))) { /*  716 */       return false; /*      */     } /*      */      /*  719 */     this.mStarted = true; /*      */      /*      */  /*  722 */     this.mDeviceMonitor = new DeviceMonitor(this); /*  723 */     this.mDeviceMonitor.start(); /*      */      /*  725 */     return true; /*      */   } 
線程的啟動是在我們之前見過的AdbDebugBridge里面,一旦adb啟動,就會去調(diào)用構(gòu)造函數(shù)去初始化DeviceMonitor實例,并調(diào)用實例的上面這個start方法來啟動一個線程。

/*      */   boolean start() /*      */   { /*  715 */     if ((this.mAdbOsLocation != null) && (sAdbServerPort != 0) && ((!this.mVersionCheck) || (!startAdb()))) { /*  716 */       return false; /*      */     } /*      */      /*  719 */     this.mStarted = true; /*      */      /*      */  /*  722 */     this.mDeviceMonitor = new DeviceMonitor(this); /*  723 */     this.mDeviceMonitor.start(); /*      */      /*  725 */     return true; /*      */   }
該線程會進行一個無限循環(huán)來檢測設(shè)備的變動。

private void deviceMonitorLoop() /*     */   { /*     */     do /*     */     { /*     */       try /*     */       { /* 161 */         if (this.mMainAdbConnection == null) { /* 162 */           Log.d("DeviceMonitor", "Opening adb connection"); /* 163 */           this.mMainAdbConnection = openAdbConnection(); /* 164 */           if (this.mMainAdbConnection == null) { /* 165 */             this.mConnectionAttempt += 1; /* 166 */             Log.e("DeviceMonitor", "Connection attempts: " + this.mConnectionAttempt); /* 167 */             if (this.mConnectionAttempt > 10) { /* 168 */               if (!this.mServer.startAdb()) { /* 169 */                 this.mRestartAttemptCount += 1; /* 170 */                 Log.e("DeviceMonitor", "adb restart attempts: " + this.mRestartAttemptCount); /*     */               } /*     */               else { /* 173 */                 this.mRestartAttemptCount = 0; /*     */               } /*     */             } /* 176 */             waitABit(); /*     */           } else { /* 178 */             Log.d("DeviceMonitor", "Connected to adb for device monitoring"); /* 179 */             this.mConnectionAttempt = 0; /*     */           } /*     */         } /*     */          /* 183 */         if ((this.mMainAdbConnection != null) && (!this.mMonitoring)) { /* 184 */           this.mMonitoring = sendDeviceListMonitoringRequest(); /*     */         } /*     */          /* 187 */         if (this.mMonitoring) /*     */         { /* 189 */           int length = readLength(this.mMainAdbConnection, this.mLengthBuffer); /*     */            /* 191 */           if (length >= 0) /*     */           { /* 193 */             processIncomingDeviceData(length); /*     */              /*     */  /* 196 */             this.mInitialDeviceListDone = true; /*     */           } /*     */         } /*     */       } /*     */       catch (AsynchronousCloseException ace) {}catch (TimeoutException ioe) /*     */       { /* 202 */         handleExpectionInMonitorLoop(ioe); /*     */       } catch (IOException ioe) { /* 204 */         handleExpectionInMonitorLoop(ioe); /*     */       } /* 206 */     } while (!this.mQuit); /*     */   } 
一旦發(fā)現(xiàn)設(shè)備有變動,該循環(huán)會立刻調(diào)用processIncomingDeviceData這個方法來更新設(shè)備信息

/*     */   private void processIncomingDeviceData(int length) throws IOException /*     */   { /* 298 */     ArrayList list = new ArrayList(); /*     */      /* 300 */     if (length > 0) { /* 301 */       byte[] buffer = new byte[length]; /* 302 */       String result = read(this.mMainAdbConnection, buffer); /*     */        /* 304 */       String[] devices = result.split("\n"); /*     */        /* 306 */       for (String d : devices) { /* 307 */         String[] param = d.split("\t"); /* 308 */         if (param.length == 2) /*     */         { /* 310 */           Device device = new Device(this, param[0], IDevice.DeviceState.getState(param[1])); /*     */            /*     */  /*     */  /* 314 */           list.add(device); /*     */         } /*     */       } /*     */     } /*     */      /*     */  /* 320 */     updateDevices(list); /*     */   }
該方法首先會取得所有的device列表(類似"adb devices -l"命令獲得所有device列表),然后調(diào)用updateDevices這個方法來對所有設(shè)備信息進行一次更新:

 private void updateDevices(ArrayList newList) /*     */   { /* 329 */     synchronized () /*     */     { /*     */  /*     */  /* 333 */       ArrayList devicesToQuery = new ArrayList(); /* 334 */       synchronized (this.mDevices) /*     */       { /*     */  /*     */  /*     */  /*     */  /*     */  /*     */  /*     */  /*     */  /* 344 */         for (int d = 0; d < this.mDevices.size();) { /* 345 */           Device device = (Device)this.mDevices.get(d); /*     */            /*     */  /* 348 */           int count = newList.size(); /* 349 */           boolean foundMatch = false; /* 350 */           for (int dd = 0; dd < count; dd++) { /* 351 */             Device newDevice = (Device)newList.get(dd); /*     */              /* 353 */             if (newDevice.getSerialNumber().equals(device.getSerialNumber())) { /* 354 */               foundMatch = true; /*     */                /*     */  /* 357 */               if (device.getState() != newDevice.getState()) { /* 358 */                 device.setState(newDevice.getState()); /* 359 */                 device.update(1); /*     */                  /*     */  /*     */  /* 363 */                 if (device.isOnline()) { /* 364 */                   if ((AndroidDebugBridge.getClientSupport()) &&  /* 365 */                     (!startMonitoringDevice(device))) { /* 366 */                     Log.e("DeviceMonitor", "Failed to start monitoring " + device.getSerialNumber()); /*     */                   } /*     */                    /*     */  /*     */  /*     */  /* 372 */                   if (device.getPropertyCount() == 0) { /* 373 */                     devicesToQuery.add(device); /*     */                   } /*     */                 } /*     */               } /*     */                /*     */  /* 379 */               newList.remove(dd); /* 380 */               break; /*     */             } /*     */           } /*     */            /* 384 */           if (!foundMatch) /*     */           { /*     */  /* 387 */             removeDevice(device); /* 388 */             this.mServer.deviceDisconnected(device); /*     */           } /*     */           else { /* 391 */             d++; /*     */           } /*     */         } /*     */          /*     */  /*     */  /* 397 */         for (Device newDevice : newList) /*     */         { /* 399 */           this.mDevices.add(newDevice); /* 400 */           this.mServer.deviceConnected(newDevice); /*     */            /*     */  /* 403 */           if ((AndroidDebugBridge.getClientSupport()) &&  /* 404 */             (newDevice.isOnline())) { /* 405 */             startMonitoringDevice(newDevice); /*     */           } /*     */            /*     */  /*     */  /* 410 */           if (newDevice.isOnline()) { /* 411 */             devicesToQuery.add(newDevice); /*     */           } /*     */         } /*     */       } /*     */        /*     */  /* 417 */       for (Device d : devicesToQuery) { /* 418 */         queryNewDeviceForInfo(d); /*     */       } /*     */     } /* 421 */     newList.clear(); /*     */   } 
該方法我們關(guān)注的是最后面它會循環(huán)每個設(shè)備,然后調(diào)用queryNewDeviceForInfo這個方法去更新每個設(shè)備所有的porperty信息。

/*     */   private void queryNewDeviceForInfo(Device device) /*     */   { /*     */     try /*     */     { /* 446 */       device.executeShellCommand("getprop", new GetPropReceiver(device)); /*     */        /*     */  /* 449 */       queryNewDeviceForMountingPoint(device, "EXTERNAL_STORAGE"); /* 450 */       queryNewDeviceForMountingPoint(device, "ANDROID_DATA"); /* 451 */       queryNewDeviceForMountingPoint(device, "ANDROID_ROOT"); /*     */        /*     */  /* 454 */       if (device.isEmulator()) { /* 455 */         EmulatorConsole console = EmulatorConsole.getConsole(device); /* 456 */         if (console != null) { /* 457 */           device.setAvdName(console.getAvdName()); /* 458 */           console.close(); /*     */         } /*     */       } /*     */     } catch (TimeoutException e) { /* 462 */       Log.w("DeviceMonitor", String.format("Connection timeout getting info for device %s", new Object[] { device.getSerialNumber() })); /*     */  /*     */     } /*     */     catch (AdbCommandRejectedException e) /*     */     { /* 467 */       Log.w("DeviceMonitor", String.format("Adb rejected command to get  device %1$s info: %2$s", new Object[] { device.getSerialNumber(), e.getMessage() })); /*     */  /*     */     } /*     */     catch (ShellCommandUnresponsiveException e) /*     */     { /* 472 */       Log.w("DeviceMonitor", String.format("Adb shell command took too long returning info for device %s", new Object[] { device.getSerialNumber() })); /*     */  /*     */     } /*     */     catch (IOException e) /*     */     { /* 477 */       Log.w("DeviceMonitor", String.format("IO Error getting info for device %s", new Object[] { device.getSerialNumber() })); /*     */     } /*     */   }
到了這里我們終于看到了該方法調(diào)用了一個ddmlib庫的device類里面的executeShellCommand方法來執(zhí)行‘getprop'這個命令。到目前位置我們達到的目的是知道了getSystemProperty這個MonkeyDevice的api最終確實是通過發(fā)送'adb shell getporp‘命令來獲得設(shè)備屬性的。

但這里遺留了兩個問題

  • 一個是之前提到的GetPropertyReceiver這個類里面的增加property的processNewLines方法是在哪里調(diào)用的
  • 一個是executeShellCommand究竟是怎么工作的

各位看官不用著急,且看我們往下分析,很快就會水落石出了。我們繼續(xù)跟蹤executeShellCommand這個方法,在我們的例子中其以命令'getprop'和new的GetPropertyReceiver對象實例為參數(shù),最終會調(diào)用到Device這個類里面的executeShellCommand這個方法。注意這個GetPropertyReceiver很重要,我們往后會看到。

/*      */   public void executeShellCommand(String command, IShellOutputReceiver receiver, int maxTimeToOutputResponse) /*      */     throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException, IOException /*      */   { /*  618 */     AdbHelper.executeRemoteCommand(AndroidDebugBridge.getSocketAddress(), command, this, receiver, maxTimeToOutputResponse); /*      */   }
方法中繼續(xù)把調(diào)用直接拋給AdbHelper這個工具類,

/*     */   static void executeRemoteCommand(InetSocketAddress adbSockAddr, String command, IDevice device, IShellOutputReceiver rcvr, long maxTimeToOutputResponse, TimeUnit maxTimeUnits) /*     */     throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException, IOException /*     */   { /* 378 */     long maxTimeToOutputMs = 0L; /* 379 */     if (maxTimeToOutputResponse > 0L) { /* 380 */       if (maxTimeUnits == null) { /* 381 */         throw new NullPointerException("Time unit must not be null for non-zero max."); /*     */       } /* 383 */       maxTimeToOutputMs = maxTimeUnits.toMillis(maxTimeToOutputResponse); /*     */     } /*     */      /* 386 */     Log.v("ddms", "execute: running " + command); /*     */      /* 388 */     SocketChannel adbChan = null; /*     */     try { /* 390 */       adbChan = SocketChannel.open(adbSockAddr); /* 391 */       adbChan.configureBlocking(false); /*     */        /*     */  /*     */  /*     */  /* 396 */       setDevice(adbChan, device); /*     */        /* 398 */       byte[] request = formAdbRequest("shell:" + command); /* 399 */       write(adbChan, request); /*     */        /* 401 */       AdbResponse resp = readAdbResponse(adbChan, false); /* 402 */       if (!resp.okay) { /* 403 */         Log.e("ddms", "ADB rejected shell command (" + command + "): " + resp.message); /* 404 */         throw new AdbCommandRejectedException(resp.message); /*     */       } /*     */        /* 407 */       byte[] data = new byte['            
            
                        
網(wǎng)頁題目:MonkeyRunner源碼分析之與Android設(shè)備通訊方式
轉(zhuǎn)載來源:http://weahome.cn/article/ggoceo.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部