It may be strange to see why the monkey process that started the target device is placed after the section "Run test Scripts".
Looking at the start-up process for the entire monkeyrunner, we see no mention of where the monkey process started. Then it is strange, when Monkey was Monkeyrunner start up?
Our test scripts need to execute a call almost without exception at the outset: monkeyrunner.waitforconnection (), if more than one device is connected to the host, you also need to specify the device serial number, and you can specify the timeout time to wait for the connection, such as: Monkeyrunner.waitforconnection ("xxx", 10000), where xxx represents the serial number, 10000 represents a time-out timeout of 10 seconds, and the framework provides default parameters if no parameters are specified. In fact, Monkey's start-up is done in some of the Waitforconnection's column calls.
Monkeyrunner What is the purpose of calling Waitforconnection? As the name implies, of course, waiting to connect the device. But I think it's wrong to think, in the above Monkeyrunner boot run, not already in the boot device monitoring thread devicemonitor to create a mdevices list of device types that represent the target devices? Indicates that the device is connected. Even if we don't run any monkeyrunner scripts, sending the "ADB devices" command can also list all the devices that are already connected. So did waitforconnection name Google wrong? Actually not, here waitforconnection really is waiting for the connection of the device, just waiting for is not the connection of device devices above, because it is true that this device is connected when the device is started to monitor the thread, The wait here is to connect the device to the Adbchimpdevice. Everyone here may be confused, what is this device? There are actually a few abstract device classes here (Abstract device class here is not to say that these classes are abstract, but that these classes represent the device is really a virtual machine on the host side of the abstract), if I do the following parsing people should be very good understanding:
- Device (Ddmlib): Represents devices that are controlled through the ADB
- Chimpmanager (Chimpchat): Represents a device that is controlled through money
- Adbchimpdevice (Chimpchat): is an abstraction of a higher-level device concept that contains the above-controlled device devices via ADB, and also includes the above devices controlled by monkey, When the call comes in, Adbchimpdevice will decide which device to use, depending on whether it needs to send a command to the ADB server or to monkey.
- Monkeydevice (Monkeyrunner): A high-level abstraction device that can be used directly by a test script, and all of its APIs are basically distributed to Adbchimpdevice to execute.
Let's look at the relationship between Adbchimpdevice and device and Chimpmanager in the following class diagram: Adbchimpdevice has two key member variables, one is the idevice type, is actually an instance of the device's parent class; One is an instance of the Chimpmanager type. In the next chapter, when we analyze the implementation of Monkeydevice, we will see a call from Monkeydevice, such as press, which is eventually distributed to Adbchimpdevice, Adbchimpdevice decides whether to send commands through Chimpmanager monkey or via device to the ADB server.
Figure 8-6-1 Key classes involved in starting the monkey process
We first analyze the relationship of each class in the class diagram and briefly describe how the Monkeyrunner starts monkey, and then analyze their associated implementation code.
- Monkeyrunner: Each test script believes it will first use the class's Waitforconnection method to get a Monkeydevice object to come and go, and so on, but then in the script this class is basically missing. It has a chimpchat instance variable chimpchat, which we have already analyzed that was set when the Monkeyrunnerstarter was instantiated. The Waitforconnection method will eventually invoke the Waitforconnection method of the Chimpchat class by chimpchat this instance
- Monkeydevice: It can be said that the script uses the most frequent of the class, the basic and target devices to interact with the operation is done by calling it. It has a large number of methods for manipulating target devices, such as Press,takesnapshot,type. Note that it has a ichimpdevice type of Impl instance, and through the following analysis we will see that it is actually an example of Adbchimpdevice, because Adbchimpdevice also implements the Ichimpdevice interface. According to the above description Adbchimpdevice is a high-level abstraction of the device, it has an instance of Chimpmanager can send commands to Monkey, also has a device instance can send ADB command to the ADB server
- Chimpchat: This class we have already analyzed in the above initiation process, which is instantiated in the process of monkeyrunnerstarter instantiation, and it will start Androiddebugbridge and devicemonitor in the process of instantiation. When the instantiation is complete, the Monkeyrunnerstarter constructor will finally call Monkeyrunner's Setchimpchat static method to set it to the chimpchat of the Monkeyrunner member variable. Chimpchat has a ichimpbackend type of mbackend object, in fact, is a Adbbackend object, because according to the previous initiation process analysis, When the Chimpchat constructor finishes instantiating the Adbbackend object, it saves the object to the Mbackend member object. The Waitforconnection method of Chimpchat is to invoke Adbbackend's Waitforconnection method through this object.
- Adbbackend: This class has also been analyzed in the above initiation process, which is instantiated in the process of instantiating chimpchat, and it instantiates the Androiddebugbridge in the process of instantiation. So it holds an example of a androiddebugbridge. With this example adbbackend can call Androiddebugbridge's GetDevices method to get a list of all device devices that are already connected, And according to the Waitforconnection incoming device serial number to find the target device devices, and then to instantiate adbchimpdevice this high-level abstraction device and return the device to the upper
- Adbchimpdevice: This is the high-level device that has been emphasized before, has an instance of device representing the ADB device, and also has a manager instance representing the monkey device. The Waitforconnection method of the Monkeyrunner class finally obtains the instance of this class, and constructs the Monkeydevice object with this instance as the parameter, During the construction process, Monkeydevice will save this instance to the IMPL member variable of the Monkeydevice object, so the press,type of the Monkeydevice class described above, API calls such as Takesnapshot can be impl this object to invoke their corresponding press,type,takesnapshot methods, and distribute these methods to monkey device Chimpmanager or ADB device devices to execute
- Chimpmanager: Represents a monkey device, which means that all monkey related requests are sent to the Chimpmanager for processing. It has several member variables dedicated to and monkey communication, such as Monkesocket is used to connect with monkey, and Monkeywriter is used to write data to the socket, that is, to send the requested Monkeyreader is used to read data to the socket, that is, to read the results of the request. It also has a lot of monkey related calls, such as touch, etc.
- Androiddebugbridge: This class is also in the previous Monkeyrunner start process has analyzed how it started up, the main function described above is to start the ADB server and Devicemonitor. Its main function in this section will be that it maintains the devicemonitor instance of Mdevicemonitor, so it can get the latest device list for devicemonitor maintenance. Why get this list, see the description of the Adbbackend class above
- Devicemonitor: The main function of this class here is to provide getdevices this method to obtain the latest list of device devices it maintains
With the following basic knowledge, we can analyze the code to illustrate how monkey is started in a series of calls that the user calls Monkeyrunner.waitforconnection, Let's take a look at this waitforconnection method of Monkeyrunner:
monkeydevice waitforconnection (pyobject[] args, string[] kws) (+) + argparser ap = Jythonu Tils.createargparser (args, KWS); Preconditions.checknotnull (AP); long Timeoutms; try-n-P { timeoutinsecs = Jythonutils.getfloat (AP, 0); Timeoutms = (timeoutinsecs * 1000.0D); \ n } catch (Pyexception e) { Timeoutms = Long.max_value; 73
Ichimpdevice device = chimpchat.waitforconnection (Timeoutms, ap.getstring (1, ". *"); 77 Monkeydevice chimpdevice = new Monkeydevice (device), return chimpdevice;
Code 8-6-1 monkeyrunner-waitforconnection
These classes, such as scripts called Monkeyrunner,monkeydevice, are written in Java, and the scripts themselves are written through Jython (which can be called Java Python), so their previous arguments need to have a mechanism for conversion. As to how they are converted is not the focus, so I myself have not studied the implementation principle of Jython, because this does not affect my understanding of the Monkeyrunner framework. OK, let's continue the analysis of the above code:
- Line 75: Call the Waitforconnection method of the Chimpchat object to get a Adbchimpdevice instance of a high-level abstract device
- 79-78 rows: Pass the above Adbchimpdevice instance as a parameter into Monkeydevice to construct a Monkedevice object and return it to the test code so that the test code can manipulate the Monkeydevice instance to control the target device.
We analyzed the Waitforconnection method of Chimpchat, and as for the Monkeydevice constructor, we will make a detailed analysis of the operation principle of the whole monkeydevice in the next chapter, so there is no repetition here.
Ichimpdevice waitforconnection (Long Timeoutms, String deviceId) This.mBackend.waitForConnection (Timeoutms, deviceId); 98 public ichimpdevice waitforconnection () this.mBackend.waitForConnection ( 2147483647L, ". *"); 101 }
Code 8-6-2 chimpchat-waitforconnection
Chimpchat provides two waitforconnection methods, one of which is without parameters, equivalent to the user calling Monkeydevice.waitforconnection () directly in the script; the other is a long-type timeout with a parameter and a device serial number. No matter which method Chimpchat is very simple only one row , call is Mbackend waitforconnection, but if the user does not provide parameters, Chimpchat will default initialization time-out and the device serial number parameters, Where the device serial number is initialized to a regular expression ". *", which represents any device first found. The mbackend here is the Adbbackend object that was instantiated in the previous analysis of the "Start Androiddebugbridge" process, so we are going to navigate to the Waitforconnection method of the class.
Ichimpdevice waitforconnection (Long Timeoutms, String Deviceidregex) 117 {118 do {119 IDevice device = Findattacheddevice (Deviceidregex), 121 if (device! = null) && (device.getstate () = = Idev Ice. Devicestate.online) {122 ichimpdevice chimpdevice = new Adbchimpdevice (device); 123 This.devices.add ( Chimpdevice); 124 return chimpdevice;125 }126 try127 {thread.sleep (200L); 129 } catch (Interruptedexception e) { LOG.log (level.severe, "Error sleeping", e); 131 }132 Timeoutms- = 200l;133 } while (Timeoutms > 0L) 134 135 136 return null;137 }
Code 8-6-3 adbbackend-waitforconnection
The method does a while loop that waits until a device that matches the user's or default serial number knows the timeout, and if it finds the device, it constructs a Adbchimpdevice instance object with the device as a parameter and returns:
- 133 Line: Enter the while loop, and if the target device is not connected within the timeout period, keep looping
- 128 and 132 rows: every cycle if no target device is connected, sleep 0.2 seconds before the next cycle
- 119 Line: Find the target device unit based on the supplied device serial number (regular expression), where can I find it? Actually, it's going to devicemonitor. Monitor thread object maintenance in the latest device devices queue to find
- 122 rows: After locating the target device, the device object is passed into the Adbchimpdevice constructor to instantiate the Androidchimpdevice object and return. We'll see that Adbchimpdevice will implement the Chimpmanager object to initiate monkey communication during instantiation.
We will focus on the first analysis of 119 rows Findattacheddevice is how to find the target device object according to the serial number, and then analyze how the Adbchimpdevice is constructed. Let's go into the Findattacheddevice method:
IDevice Findattacheddevice (String Deviceidregex) {101 pattern pattern = pattern.compile ( Deviceidregex); 102 for (IDevice device:this.bridge.getDevices ()) {103 String serialnumber = Device.getserialnumber (); 104 if (Pattern.matcher (serialnumber). Matches ()) {device;106 }107 }108 return null;109 }
Code 8-6-4 adbbackend-findattacheddevice
In the 3rd section of the analysis of the "start Androiddebugbridge" process we have learned that adbbackend after initializing the Androiddebugbridge object will save the instance to its own bridge member object, So the thing that this method does is to get all the latest device devices list (102 rows) through this Androiddebugbridge instance, and then match the serial number and parameters provided for each device, and return (103-105 lines) if yes.
Let's take a look at how to get a list of devices:
482 public idevice[] getDevices () 483 {484 synchronized (slock) {485 if (this.mdevicemonitor! = null) { 486 return this.mDeviceMonitor.getDevices (); 487 } 488 } 489 490 return new idevice[0]; 491 }
Code 8-6-5 adbbackend-getdevices
Androiddebugbridge object is started Devicemonitor device Monitoring thread object will save the object to Mdevicemonitor member variables, which we have learned in the previous. The GetDevices method of Androiddebugbridge here is to call its GetDevices method in the Devicemonitor object to get the latest list of devices:
129 device[] GetDevices () {131 synchronized (this.mdevices) {device[ ] This.mDevices.toArray (New Device[this.mdevices.size ()]); 133 }134 }
Code 8-6-6 devicemonitor-getdevices
In the 4th section, "Start device monitoring thread Devicemonitor" We have learned that when Devicemonitor sends a monitoring command "host:track-devices" to the ADB server, once the device is monitored for new additions or removal or status changes, The device's changes will be saved to the Mdevices list. This entire list is returned by the above method.
Code 8-6-4 Adbbackend-findattacheddevice, findattacheddevice after acquiring the device list, the serial number of each device is removed to compare with the target device serial number to find the matching device directly to return, So let's look at the corresponding method to get the serial number Device.getserialnumber ():
244 Public String getserialnumber () 245 {246 return this.mserialnumber; 247 }
Code 8-6-7 device-getserialnumber
The direct return is the Mserialnumber variable saved by device devices, which represents the serial number of the corresponding device. You should remember that when you analyze the "Processincomingdevicedata" method in section 4th, "Boot device Monitoring thread Devicemonitor", Once the ADB server sends out the latest device list, it takes out the serial number and device status of each device to initialize the device device to keep the serial number and device status of the unit in the corresponding Mserialnumber and MSTAT member variables of the devices device.
After the device serial number is obtained, the findattacheddevice is compared to the supplied serial number, and if the match is returned to the caller "code 8-6-3 adbbackend-waitforconnection". The Adbbackend Waitforconnection, after acquiring the device instance, will pass it to the Adbchimpdevice constructor to construct the Adbchimpdevice instance object. Let's see how its constructor is done:
Adbchimpdevice (IDevice device) { this.device = device; This.manager = Createmanager ( "127.0.0.1", 12345); preconditions.checknotnull (this.manager);
Code 8-6-8 adbchimpdevice Constructors
As has been emphasized previously, Adbchimpdevice is a very important class, a high-level abstract device object that combines devices that represent device control through Monkey Chimpmanager and device control via ADB. This combination is represented by the above-mentioned Adnbchimpdevice constructor function. The 70th line combines device devices, and the 71-row combination is the Chimpmanager instance. It is just that the device instance was instantiated in the boot-up monitoring thread devicemonitor, and Chimpmanager was created at this time. When created, a native loopback IP address "127.0.0.1" is specified, and the port designation is monkey local forwarding port 12345
The code to create the Chimpmanager call Createmanager is a bit long, and we'll analyze it in two parts, the first of which is start monkey, and the second part creates Chimpmanager. Let's look at the first part:
123 Private Chimpmanager Createmanager (String address, int port) {124 try {This.device.createForward (port, Port); 126} catch (TimeoutException e) {127 LOG.log (level.severe, "Timeout creating adb port Forwarding", e); 12 8 return null;129} catch (Adbcommandrejectedexception e) {LOG.log (Level.severe, "ADB rejected adb por T forwarding command: "+ e.getmessage (), e); 131 return null;132} catch (IOException e) {133 LOG.log (leve L.severe, "Unable to create ADB port forwarding:" + e.getmessage (), e); 134 return null;135}136 137 String Command = "Monkey--port" + port;138 executeasynccommand (command, New Loggingoutputreceiver (LOG, Level.fine)); 139 140 try141 {142 Thread.Sleep (1000L); 143} catch (Interruptedexception e) {144 LOG.log (Level.severe, " Unable to sleep ", e); 145}146 inetaddress addr;147 try148 {149 addr = inetaddress.getbyname (address) ; Unknow catch (Nhostexception e) {151 LOG.log (Level.severe, "Unable to convert address into inetaddress:" + address, e); 152 Return null;153} ...}
code 8-6-9 adbchimpdevice-createmanager start Monkey
The first thing Createmanager do is to get the monkey service process on the target device to start up and receive the Monkeyrunner test script to send past requests. The code flow is as follows:
- 125 line: Set the local to target machine monkey process listening port forwarding, call the device's Createforward method, this method we describe the device class in the next chapter when the analysis. All it takes here is that it basically can be seen as sending "adb forward 12345 12345" on the command line to complete the forwarding of 12345 ports from the native 12345 port to the remote Monkey Monitor. After setting up port forwarding, the code can directly connect to the 12345 port of this machine, which is equivalent to the 12345 port of monkey listening in the remote target device.
- 139-138 Line: After setting the monkey port forwarding, the Createmanager method sends the shell command "monkey--port 12345" to the ADB server to start monkey to listen on port 12345. The method used to send the ADB shell command is the Createasynccommand method, in fact, there is no good analysis of the method, because it sends the command request directly to the device class Executeshellcommand just, And Executeshellcommand This method, we will analyze it in the next chapter.
- 149 Line: Convert the native listening address "127.0.0.1" to the InetAddress object format so that the socket connection can be created directly using
Createmanager start monkey here to complete, down we continue to see the second part of the creation of the Createmanager Chimpmanager:
123 Private Chimpmanager Createmanager (String address, int port) {...//start Monkey code slightly 159 Boolean success = false;160 Chimpmanager mm = null;161 long start = System.currenttimemillis () 162 163 while (!success) {164 Long Now = System.currenttimemillis (); 165 long diff = now-start;166 if (diff > 30000L) {167 Log.severe ("T Imeout while trying to create chimp Mananger "); 168 return null;169}170 try171 {172 thre Ad.sleep (1000L); 173} catch (Interruptedexception e) {174 LOG.log (Level.severe, "Unable to Sleep", e); 175 }176 socket monkeysocket;177 try178 {179 monkeysocket = new Socket (addr, port); Atch (IOException e) {181 LOG.log (Level.fine, "Unable to connect socket", e); 182 success = FALSE;} 183 continue;184 185 try186 {187 mm = new Chimpmanager (monkeysocket); 188} catch (IOE Xception e) {189 Log.loG (Level.severe, "unable to open writer and reader to Socket"); }190 continue;191 192 try193 {194 mm.wake (); 195} catch (IOException e) {196 LOG.log (Level.fine, "Unable to wake up device", e); 197 success = false; }198 continue;199 success = true;201}202 203 return mm;}
Code 8-6-10 adbchimpdevice-createmanager creation Chimpmanager
In fact, the above heap of code is nothing more than doing 3 things in a loop:
- 179 row: Create a Socket object that is connected to the native monkey to the originating port
- 187 Line: Based on the socket object construction Chimpmanager instance, Chimpmanager's detailed analysis will be placed in the next chapter of the description Chimpmanager class in detail when the analysis
- 194 Line: Send the command to monkey to wake the hibernation screen if the screen is in hibernation. The wake principle will be analyzed in the next chapter.
The objective of analyzing this section here has been achieved, and we have learned how the Monkey service process was started in the script by invoking the Waitforconnection method of Monkeyrunner. We also learned about the knowledge points created by the two key classes of Adbchimpdevice and Chimpmanager.
In the next section we will try to summarize what we have learned in this chapter.
Note: More articles please pay attention to the public number: Techgogogo or personal blog http://techgogogo.com. Of course, you are also very welcome to pick up directly (ZHUBAITIAN1). This article by Heaven Zhuhai Branch Rudder original. Reproduced please consciously, whether the complaint rights to look at the mood.
8th Chapter 6 "Monkeyrunner Source Analysis" Monkeyrunner start run process-Start monkey