中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

MonkeyRunner源碼分析之與Android設備通訊方式

發布時間:2020-05-26 18:17:46 來源:網絡 閱讀:483 作者:zhukev 欄目:移動開發

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

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


1.概述

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

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

以下是MonkeyDevice所有請求對應的與設備通信方式

請求

是否需要和目標設備通信

通信方式

注解

發送adb shell命令

getSystemProperty

發送adb shell命令


installPackage

發送adb shell命令

傳送數據時發送adb協議請求,發送安裝命令時使用adb shell命令

startActivity

發送adb shell命令


broadcastIntent

發送adb shell命令


instrument

發送adb shell命令


shell

發送adb shell命令

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

removePackage

發送adb shell命令


發送monkey命令

getProperty

發送monkey命令  


wake

發送monkey命令  


dispose 

發送monkey命令   


press

發送monkey命令  


type

發送monkey命令  


touch

發送monkey命令  


drag

發送monkey命令  


getViewIdList

發送monkey命令  


getView

發送monkey命令  


getViews

發送monkey命令  


getRootView

發送monkey命令  


發送adb協議請求

takeSnapshot

發送adb協議請求


reboot

發送adb協議命令


installPackage

發送adb協議請求

相當于直接發送adb命令行命令’adb push’

分析之前請大家準備好對應的幾個庫的源碼:

MonkeyRunner源碼分析之與Android設備通訊方式

2. 發送monkey命令

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

/*     */   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())); /*     */   }
初始化所做的事情如下

  • 把構造函數傳進來的monkeySocket這個socket對象保存起來,往下會分析這個socket是如何創立的
  • 初始化monkeyWriter這個BufferedWriter,今后往monkey的socket發送命令的時候用的就是它
  • 初始化monkeyReader這個BufferedReader,今后從monkey的socket讀返回的時候用的就是它
好,那么現在我們返回來看這個類是什么時候實例化的。請定位到AdbChimpDevice的構造函數:

/*     */   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構造的時候已經開始初始化的了,初始化傳入的地址是"127.0.0.1"和端口是12345,這個是在下面分析的createManager這個方法中創建socket用的,也就是我們上面提到的monkeySocket.在繼續之前這里我們先整理下思路,結合上一篇文章,我們看到幾個重要的類的初始化流程是這樣的:
  • MonkeyRunner在啟動的時候會先啟動MonkeyRunnerStarter這個類,該類的構造函數調用ChimpChat的getInstance方法實例化ChimpChat.
  • ChimpChat的getInstance方法會先實例化AdbBackend這個類,然后構建 ChimpChat自身這個實例
  • 用戶調用MonkeyRunner.waitForConnection()方法初始化MonkeyDevice
  • 以上的waitForConnection()又調用的是ChimpChat的waitforConnection()方法
  • ChimpChat的waitForConnection方法調用的是AdbBackend的waitForConnection方法最終會findAttachedDevice找到目標設備然后用該設備初始化AdbChimpDevice
根據以上的流程我們就很清晰AdbChimpDevice其實在測試腳本一調用MonkeyRunner.waitForConnection方法的時候就已經會初始化的了,也就是說ChimpManager也在這個時候已經初始化的了。

好,那么我們繼續看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; /*     */   }
這個方法比較長,但大體做的事情如下:

  • 通過調用ddmlib的device類里面的createForward方法來把主機pc端本地的端口轉發給目標機器端的monkey監聽端口,這樣子做的好處是我們通過直接連接主機pc端的轉發端口發送命令就會等同于通過網絡連接上目標機器的monkey監聽端口來發送monkey命令
  • 調用executeAsyncCommand方法發送異步adb shell命令 “monkey -port"到目標機器開啟monkey并監聽以上描述的端口
  • 創建連接到主機pc對應目標設備monkey監聽端口的monkeySocket
  • 把該monkeySocket傳遞到本章節開頭說的ChimpManager構造函數對ChimpManager進行實例化
分析到這里我們可以看到monkey已經在目標機器起來了,那么我們就需要去分析MonkeyRunner是如何發送monkey命令過去控制設備的了。這里我們會以典型的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); /*     */     } /*     */   }
方法很簡單,就是根據不同的按下類型來調用ChimpManager中不同的press的方法,我們這里假設用戶按下的是 DOWN_AND_UP這個類型,也就是說調用的是ChimpMananer里面的press方法:
/*     */   public boolean press(String name) /*     */     throws IOException /*     */   { /* 135 */     return sendMonkeyEvent("press " + name); /*     */   }
跟著調用sendMonkeyEvent:
/*     */   private boolean sendMonkeyEvent(String command) /*     */     throws IOException /*     */   { /* 234 */     synchronized (this) { /* 235 */       String monkeyResponse = sendMonkeyEventAndGetResponse(command); /* 236 */       return parseResponseForSuccess(monkeyResponse); /*     */     } /*     */   }
跟著調用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這邊的終會轉發給目標機器monkey那個端口(其實就是上面的monkeySocket)進行讀寫操作。


3. 發送adb協議請求

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

4. 發送adb shell命令


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

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

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

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

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

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

/*      */   void addProperty(String label, String value) { /*  779 */     this.mProperties.put(label, value); /*      */   }
那么這個addProperty又是在哪里被調用了呢?一番查看后發現是在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的地方是知道了,但是問題是什么時候增加呢?這里我們先賣個關子。

繼續之前我們先要了解下ddmlib這個庫里面的DeviceMonitor這個類,這個類會啟動一個線程來監控所有連接到主機的設備的狀態。

/*      */   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啟動,就會去調用構造函數去初始化DeviceMonitor實例,并調用實例的上面這個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; /*      */   }
該線程會進行一個無限循環來檢測設備的變動。

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); /*     */   } 
一旦發現設備有變動,該循環會立刻調用processIncomingDeviceData這個方法來更新設備信息

/*     */   private void processIncomingDeviceData(int length) throws IOException /*     */   { /* 298 */     ArrayList<Device> 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列表),然后調用updateDevices這個方法來對所有設備信息進行一次更新:

 private void updateDevices(ArrayList<Device> newList) /*     */   { /* 329 */     synchronized () /*     */     { /*     */  /*     */  /* 333 */       ArrayList<Device> 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(); /*     */   } 
該方法我們關注的是最后面它會循環每個設備,然后調用queryNewDeviceForInfo這個方法去更新每個設備所有的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() })); /*     */     } /*     */   }
到了這里我們終于看到了該方法調用了一個ddmlib庫的device類里面的executeShellCommand方法來執行‘getprop'這個命令。到目前位置我們達到的目的是知道了getSystemProperty這個MonkeyDevice的api最終確實是通過發送'adb shell getporp‘命令來獲得設備屬性的。

但這里遺留了兩個問題

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

各位看官不用著急,且看我們往下分析,很快就會水落石出了。我們繼續跟蹤executeShellCommand這個方法,在我們的例子中其以命令'getprop'和new的GetPropertyReceiver對象實例為參數,最終會調用到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); /*      */   }
方法中繼續把調用直接拋給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['
													            
向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

普兰县| 额尔古纳市| 闽清县| 自贡市| 错那县| 武鸣县| 桐乡市| 遵化市| 高要市| 泗阳县| 呼图壁县| 金堂县| 乌拉特后旗| 榆树市| 澄迈县| 海原县| 石渠县| 巴里| 秭归县| 六枝特区| 阜阳市| 滕州市| 万盛区| 华阴市| 临洮县| 堆龙德庆县| 宜兴市| 界首市| 右玉县| 灵寿县| 英超| 吴堡县| 阳东县| 桐乡市| 陵水| 眉山市| 虎林市| 久治县| 大田县| 门源| 区。|