monkey.java::run()第五部分源码——monkey不同运行过程
monkey有4种不同的处理情况:MonkeySourceScript(),MonkeySourceRandomScript(),MonkeySourceNetwork(),MonkeySourceRandom(),接下来我们具体来看一下这4个都是如何处理的。
1.MonkeySourceScript()——脚本运行
mRandom = new Random(mSeed);
if (mScriptFileNames != null && mScriptFileNames.size() == 1) {
// script mode, ignore other options
mEventSource = new MonkeySourceScript(mRandom, mScriptFileNames.get(0), mThrottle,
mRandomizeThrottle, mProfileWaitTime, mDeviceSleepTime);
mEventSource.setVerbose(mVerbose);
mCountEvents = false;
}
1)如果monkey参数包含“-f”且只有一个脚本,则通过MonkeySourceScript()直接运行该脚本。
2)MonkeySourceScript()对于脚本的处理,就是获取随机数、脚本名、固定延迟、随机延迟、等待时间、休眠时间,然后将随机数、固定延迟、随机延迟传给MonkeyEventQueue加入monkey事件队列。
MonkeySourceScript.java::MonkeySourceScript()
public MonkeySourceScript(Random random, String filename, long throttle,
boolean randomizeThrottle, long profileWaitTime, long deviceSleepTime) {
mScriptFileName = filename;
mQ = new MonkeyEventQueue(random, throttle, randomizeThrottle);
mProfileWaitTime = profileWaitTime;
mDeviceSleepTime = deviceSleepTime;
}
在MonkeySourceScript.java中我们也可以看到event key word,这对应于前面提到的事件:
private static final String EVENT_KEYWORD_POINTER = "DispatchPointer";
private static final String EVENT_KEYWORD_TRACKBALL = "DispatchTrackball";
private static final String EVENT_KEYWORD_ROTATION = "RotateScreen";
private static final String EVENT_KEYWORD_KEY = "DispatchKey";
private static final String EVENT_KEYWORD_FLIP = "DispatchFlip";
private static final String EVENT_KEYWORD_KEYPRESS = "DispatchPress";
private static final String EVENT_KEYWORD_ACTIVITY = "LaunchActivity";
private static final String EVENT_KEYWORD_INSTRUMENTATION = "LaunchInstrumentation";
private static final String EVENT_KEYWORD_WAIT = "UserWait";
private static final String EVENT_KEYWORD_LONGPRESS = "LongPress";
private static final String EVENT_KEYWORD_POWERLOG = "PowerLog";
private static final String EVENT_KEYWORD_WRITEPOWERLOG = "WriteLog";
private static final String EVENT_KEYWORD_RUNCMD = "RunCmd";
private static final String EVENT_KEYWORD_TAP = "Tap";
private static final String EVENT_KEYWORD_PROFILE_WAIT = "ProfileWait";
private static final String EVENT_KEYWORD_DEVICE_WAKEUP = "DeviceWakeUp";
private static final String EVENT_KEYWORD_INPUT_STRING = "DispatchString";
private static final String EVENT_KEYWORD_PRESSANDHOLD = "PressAndHold";
private static final String EVENT_KEYWORD_DRAG = "Drag";
private static final String EVENT_KEYWORD_PINCH_ZOOM = "PinchZoom";
private static final String EVENT_KEYWORD_START_FRAMERATE_CAPTURE = "StartCaptureFramerate";
private static final String EVENT_KEYWORD_END_FRAMERATE_CAPTURE = "EndCaptureFramerate";
private static final String EVENT_KEYWORD_START_APP_FRAMERATE_CAPTURE =
"StartCaptureAppFramerate";
private static final String EVENT_KEYWORD_END_APP_FRAMERATE_CAPTURE = "EndCaptureAppFramerate";
2.MonkeySourceRandomScript()——随机脚本运行
else if (mScriptFileNames != null && mScriptFileNames.size() > 1) {
if (mSetupFileName != null) {
mEventSource = new MonkeySourceRandomScript(mSetupFileName,
mScriptFileNames, mThrottle, mRandomizeThrottle, mRandom,
mProfileWaitTime, mDeviceSleepTime, mRandomizeScript);
mCount++;
} else {
mEventSource = new MonkeySourceRandomScript(mScriptFileNames,
mThrottle, mRandomizeThrottle, mRandom,
mProfileWaitTime, mDeviceSleepTime, mRandomizeScript);
}
mEventSource.setVerbose(mVerbose);
mCountEvents = false;
}
如果monkey参数包含“-f”但不止一个脚本,则通过MonkeySourceRandomScript()随机运行这些脚本,这里需要注意monkey参数中是否包含“–setup”:
1)若包含,则先运行带“–setup”的脚本,再随机运行其余脚本。setupfile一般是为了配置环境或者预置条件设置的,所以必须要最先运行,以免后续因环境问题而运行错误。
MonkeySourceRandomScript.java::MonkeySourceRandomScript(String setupFileName, ArrayList scriptFileNames…)
//Creates a MonkeySourceRandomScript instance with an additional setup script.
public MonkeySourceRandomScript(String setupFileName, ArrayList<String> scriptFileNames,
long throttle, boolean randomizeThrottle, Random random, long profileWaitTime,
long deviceSleepTime, boolean randomizeScript) {
if (setupFileName != null) {
mSetupSource = new MonkeySourceScript(random, setupFileName, throttle,
randomizeThrottle, profileWaitTime, deviceSleepTime);
mCurrentSource = mSetupSource;
}
for (String fileName: scriptFileNames) {
mScriptSources.add(new MonkeySourceScript(random, fileName, throttle,
randomizeThrottle, profileWaitTime, deviceSleepTime));
}
mRandom = random;
mRandomizeScript = randomizeScript;
}
2)若不包含,则随机运行所有脚本。从下面的代码可以看出不包含setup的情况下是基于包含的情况上做的处理,将setupfile视为null。
MonkeySourceRandomScript.java::MonkeySourceRandomScript(ArrayList scriptFileNames…)
//Creates a MonkeySourceRandomScript instance without an additional setup script.
public MonkeySourceRandomScript(ArrayList<String> scriptFileNames, long throttle,
boolean randomizeThrottle, Random random, long profileWaitTime, long deviceSleepTime,
boolean randomizeScript) {
this(null, scriptFileNames, throttle, randomizeThrottle, random, profileWaitTime,
deviceSleepTime, randomizeScript);
}
3)MonkeySourceRandomScript本质还是脚本运行(MonkeySourceScript)
4)这里说的随机运行是通过getNextEvent()实现的,其中mCurrentSource从前面的代码可知当有setup时才被赋值
public MonkeyEvent getNextEvent() {
//如果事件源为空,则从脚本源列表中随机选择新的脚本事件源,并从中选择下一个事件。
if (mCurrentSource == null) {
int numSources = mScriptSources.size();
if (numSources == 1) {
mCurrentSource = mScriptSources.get(0);
} else if (numSources > 1 ) {
if (mRandomizeScript) {
mCurrentSource = mScriptSources.get(mRandom.nextInt(numSources));
} else {
mCurrentSource = mScriptSources.get(mScriptCount % numSources);
mScriptCount++;
}
}
}
//如果不为空时,不允许随机运行
if (mCurrentSource != null) {
MonkeyEvent nextEvent = mCurrentSource.getNextEvent();
if (nextEvent == null) {
mCurrentSource = null;
}
return nextEvent;
}
return null;
}
3.MonkeySourceNetwork()——远程运行
else if (mServerPort != -1) {
try {
mEventSource = new MonkeySourceNetwork(mServerPort);
} catch (IOException e) {
Logger.out.println("Error binding to network socket.");
return -5;
}
mCount = Integer.MAX_VALUE;
}
1)如果monkey参数包含“–port”,则通过MonkeySourceNetwork()运行远程调用
2)如何实现远程的呢?我们同样在MonkeySourceNetwork.java中的getNextEvent()中去找答案
MonkeySourceNetwork.java::getNextEvent()
public MonkeyEvent getNextEvent() {
if (!started) {
try {
startServer(); //启动,这里具体做了什么稍后详细讲解
} catch (IOException e) {
Log.e(TAG, "Got IOException from server", e);
return null;
}
started = true;
}
try {
while (true) {
// 循环获取事件队列的下一个事件
MonkeyEvent queuedEvent = commandQueue.getNextQueuedEvent();
if (queuedEvent != null) {
// dispatch the event
return queuedEvent;
}
// Check to see if we have any returns that have been deferred. If so, now that
// we've run the queued commands, wait for the given event to happen (or the timeout
// to be reached), and handle the deferred MonkeyCommandReturn.
if (deferredReturn != null) {
Log.d(TAG, "Waiting for event");
MonkeyCommandReturn ret = deferredReturn.waitForEvent();
deferredReturn = null;
handleReturn(ret);
}
String command = input.readLine();
if (command == null) {
Log.d(TAG, "Connection dropped.");
// Treat this exactly the same as if the user had
// ended the session cleanly with a done commant.
command = DONE;
}
if (DONE.equals(command)) {
// stop the server so it can accept new connections
try {
stopServer();
} catch (IOException e) {
Log.e(TAG, "Got IOException shutting down!", e);
return null;
}
// return a noop event so we keep executing the main
// loop
return new MonkeyNoopEvent();
}
// Do quit checking here
if (QUIT.equals(command)) {
// then we're done
Log.d(TAG, "Quit requested");
// let the host know the command ran OK
returnOk();
return null;
}
// Do comment checking here. Comments aren't a
// command, so we don't echo anything back to the
// user.
if (command.startsWith("#")) {
// keep going
continue;
}
// Translate the command line. This will handle returning error/ok to the user
translateCommand(command);
}
} catch (IOException e) {
Log.e(TAG, "Exception: ", e);
return null;
}
}
通过port实例化一个ServerSocket,通过port绑定到本地ip,
MonkeySourceNetwork.java::MonkeySourceNetwork()
MonkeySourceNetwork.java::startServer()
MonkeySourceNetwork.java::stopServer()
public MonkeySourceNetwork(int port) throws IOException {
// Only bind this to local host. This means that you can only
// talk to the monkey locally, or though adb port forwarding.
serverSocket = new ServerSocket(port,
0, // default backlog
InetAddress.getLocalHost());
}
private void startServer() throws IOException {
clientSocket = serverSocket.accept();
// At this point, we have a client connected.
// Attach the accessibility listeners so that we can start receiving
// view events. Do this before wake so we can catch the wake event
// if possible.
MonkeySourceNetworkViews.setup();
// Wake the device up in preparation for doing some commands.
wake();
input = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
// auto-flush
output = new PrintWriter(clientSocket.getOutputStream(), true);
}
/**
* Stop the server from running so it can reconnect a new client.
*/
private void stopServer() throws IOException {
clientSocket.close();
MonkeySourceNetworkViews.teardown();
input.close();
output.close();
started = false;
}
4.MonkeySourceRandom()——随机运行
如果既没有“-f”又没有“–setup”,则通过MonkeySourceRandom()直接随机运行。显然这是默认的方式。
else {
// random source by default
if (mVerbose >= 2) { // check seeding performance
Logger.out.println("// Seeded: " + mSeed);
}
mEventSource = new MonkeySourceRandom(mRandom, mMainApps,
mThrottle, mRandomizeThrottle, mPermissionTargetSystem);
mEventSource.setVerbose(mVerbose);
// set any of the factors that has been set
for (int i = 0; i < MonkeySourceRandom.FACTORZ_COUNT; i++) {
if (mFactors[i] <= 0.0f) {
((MonkeySourceRandom) mEventSource).setFactors(i, mFactors[i]);
}
}
// in random mode, we start with a random activity
((MonkeySourceRandom) mEventSource).generateActivity();
}