在《[MonkeyRunner源碼分析之與Android設備通訊方式](http://blog.csdn.net/zhubaitian/article/details/40295559)》中,我們談及到MonkeyRunner控制目標android設備有多種方法,其中之一就是在目標機器啟動一個monkey服務來監聽指定的一個端口,然后monkeyrunner再連接上這個端口來發送命令,驅動monkey去完成相應的工作。
當時我們只分析了monkeyrunner這個客戶端的代碼是怎么實現這一點的,但沒有談monkey那邊是如何接受命令,接受到命令又是如何處理的。
所以自己打開源碼看了一個晚上,大概有了概念。但今天網上搜索了下,發現已經有網友“chenjie”對monkey的源碼做過相應的分析了,而且文章寫得非常有概括性,應該是高手所為,果斷花了2個積分下載下來,不敢獨享,本想貼上來分享給大家,但發現pdf的文檔直接拷貝上來會丟失掉圖片,所以只好貼上下載地址:[http://download.csdn.net/download/zqilu/6884491](http://download.csdn.net/download/zqilu/6884491)
但文章主要是架構性得去描述monkey是怎么工作的,按照我自己的習慣,我還是喜歡按照自己的思維和有目的性的去了解我想要的,在這里我想要的是搞清楚monkey是如何處理monkeyrunner過來的命令的。
本文我們就先看下monkey的運行流程。
## 1. 運行環境設置
和monkeyrunner一樣,monkey這個命令也是一個shell腳本,它是在我們的目標android設備的“/system/bin/monkey”,其實這是一個android上面java程序啟動的標準流程。
~~~
base=/system
export CLASSPATH=$base/framework/monkey.jar
trap "" HUP
exec app_process $base/bin com.android.commands.monkey.Monkey $*
~~~
android中可以通過多種方式啟動java應用,通過app_process命令啟動就是其中一種,它可以幫忙注冊android JNI,而繞過dalvik以使用Native API(如我般不清楚的請百度)所做的主要事情如下:
- 設置monkey的CLASSPATH環境變量指向monkey.jar
- 通過app_process指定monkey的入口和傳進來的所有參數啟動上面CLASSPATH設定的monkey.jar
## 2.命令行參數解析
通過以上的app_process指定的monkey入口,我們可以知道我們的入口函數main是在com.android.commands.Monkey這個類里面的:
~~~
/**
* Command-line entry point.
*
* @param args The command-line arguments
*/
public static void main(String[] args) {
// Set the process name showing in "ps" or "top"
Process.setArgV0("com.android.commands.monkey");
int resultCode = (new Monkey()).run(args);
System.exit(resultCode);
}
~~~
入口函數很簡單,直接跳到run這個方法,這是一個很重要的方法,里面大概會做以下這些事情:
- 處理命令行參數
- 根據命令行參數啟動不同的事件源,也就是我們的測試事件究竟是從網絡如monkeyrunner過來的還是monkey內部的random測試數據集過來的還是腳本過來的如此之類
- 跳入runMonkeyCyncle方法針對不同的事件源開始獲取并執行不同的事件
這個方法會比較長,我們只看我們現在想要的關鍵片段,可以看到里面調用了命令行處理函數processOptions.
~~~
private int run(String[] args) {
...
if (!processOptions()) {
return -1;
}
...
}
~~~
進去之后就是很普通的讀取命令行的參數然后一個個進行解析保存了,沒有太多特別的東西,這里就直接貼出monkey的參數選項大家看看就好了:

## 3. 初始化測試事件源
如前所述,run方法里面在獲得命令行參數后會進入下一個環節,就是根據不同的參數去初始化不同的事件源
~~~
private int run(String[] args) {
...
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;
} 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;
} else if (mServerPort != -1) {
try {
mEventSource = new MonkeySourceNetwork(mServerPort);
} catch (IOException e) {
System.out.println("Error binding to network socket.");
return -5;
}
mCount = Integer.MAX_VALUE;
} else {
// random source by default
if (mVerbose >= 2) { // check seeding performance
System.out.println("// Seeded: " + mSeed);
}
mEventSource = new MonkeySourceRandom(mRandom, mMainApps, mThrottle, mRandomizeThrottle);
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();
}
...
mNetworkMonitor.start();
int crashedAtCycle = runMonkeyCycles();
mNetworkMonitor.stop();
...
}
~~~
事件源代表測試數據的事件是從哪里過來的,不同的event source會有不同的類來做相應的實現:
- **MonkeySourceNetwork.java**: 事件是從網絡如monkeyrunner過來的,處理的是《[MonkeyRunner源碼分析之與Android設備通訊方式](http://blog.csdn.net/zhubaitian/article/details/40295559)》描述的界面控制操作事件
- **MonkeySourceNetworkVars.java:**事件也是從網絡如monkeyrunner過來的,處理的是《[MonkeyRunner源碼分析之與Android設備通訊方式](http://blog.csdn.net/zhubaitian/article/details/40295559)》提到的getPropery事件
- **MonkeySourceNetworkViews.java**:事件也是從網絡如monkeyrunner過來的,處理的是《[MonkeyRunner源碼分析之與Android設備通訊方式](http://blog.csdn.net/zhubaitian/article/details/40295559)》提到的Views相關的事件
- **MonkeySourceRandom.java**:事件是從monkey內部生成的隨機事件集,也就是我們通過命令行啟動monkey測試目標app的常用方式
- **MonkeySourceRanodomeScript.java**: 上面的隨機內部數據源也可以通過指定setup腳本來創建
- **MonkeySourceScript.java**: 用戶也可以遵循一定的規則編寫monkey腳本來驅動monkey進行相關測試,與上面不同的是它不再是隨機的
往后的文章我們會針對其中一個事件源進行分析,在這里我們只需要知道這些事件源代表了事件的不同的來源,它會
- 從指定的源獲取命令
- 把命令翻譯成monkey事件然后放到命令隊列EventQueue
這里需要注意的是每一個EventSource類都是實現了MonkeyEventSource這個接口的,這個接口最重要的就是要求實現類必須實現getNextEvent這個方法來生成并獲取事件。這樣子做的好處就是屏蔽了每一個具體事件源的實現細節,其他地方的代碼只需要調用這個接口的getNextEvent方法獲得事件源就行了,而無需關心這些事件是從哪個源過來的。這些都是面向對象的面向接口編程的基礎了,大家有不清楚的最好先去了解下java的一些基本知識,這樣理解起來會快很多。
## 4. 循環執行事件
run方法根據參數從不同的事件源獲得事件并放入到EventQueue后,就會開始執行一個循環去從EventQueue里獲取事件進行執行
~~~
private int run(String[] args) {
...
int crashedAtCycle = runMonkeyCycles();
...
}
~~~
如前所述,runMonkeyCyles方法會根據不同的數據源開始一條條的獲取事件并進行執行:
~~~
/**
* Run mCount cycles and see if we hit any crashers.
* <p>
* TODO: Meta state on keys
*
* @return Returns the last cycle which executed. If the value == mCount, no
* errors detected.
*/
private int runMonkeyCycles() {
int eventCounter = 0;
int cycleCounter = 0;
boolean shouldReportAnrTraces = false;
boolean shouldReportDumpsysMemInfo = false;
boolean shouldAbort = false;
boolean systemCrashed = false;
// TO DO : The count should apply to each of the script file.
while (!systemCrashed && cycleCounter < mCount) {
...
MonkeyEvent ev = mEventSource.getNextEvent();
if (ev != null) {
int injectCode = ev.injectEvent(mWm, mAm, mVerbose);
...
}
...
}
....
}
~~~
注意這里的mEventSource就是我們上面提到的事件源的接口,它屏蔽了每個事件實現類的具體細節,我們只需要告訴這個接口我們現在需要取一條事件然后執行它,該結構根據面向對象的多態原理,就會自動取事件的實現類獲得對應的事件進行返回。
所以這里大家還需要對多態這個概念有所了解,特別是一些從手動測試轉到自動化測試的朋友,可能之前沒有接觸過太多面向對象的知識。本人以前做過開發,所以還ok。這里只是做一個善意的提醒。
獲得事件后下一步就是去執行相應的事件了,不同的事件會有不同的處理方式,或只是執行個命令,或調用WindowManager隱藏接口做事件注入等,這些都會在今后文章進行進一步闡述
這一篇文章就到此為止了,目的就是讓大家對整一個monkey執行的流程有個初步的了解,方便理解往下的相關文章。
<table cellspacing="0" cellpadding="0" width="539" class=" " style="margin:0px 0px 10px; padding:0px; border-collapse:collapse; width:668px; max-width:100%; word-wrap:break-word!important"><tbody style="margin:0px; padding:0px; max-width:100%; word-wrap:break-word!important"><tr style="margin:0px; padding:0px; max-width:100%; word-wrap:break-word!important"><td valign="top" width="112" height="39" style="border-style:solid; border-color:rgb(0,0,0); margin:0px; padding:4px; word-break:break-all; max-width:100%; word-wrap:break-word!important">?</td></tr><tr style="margin:0px; padding:0px; max-width:100%; word-wrap:break-word!important"><td valign="top" width="111" height="13" style="border-style:solid; border-color:rgb(0,0,0); margin:0px; padding:4px; word-break:break-all; max-width:100%; word-wrap:break-word!important; background-color:rgb(190,192,191)"><p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; max-width:100%; clear:both; min-height:1em; white-space:pre-wrap; word-wrap:break-word!important"><span style="margin:0px; padding:0px; max-width:100%; word-wrap:break-word!important">作者</span></p></td><td valign="top" width="112" height="13" style="border-style:solid; border-color:rgb(0,0,0); margin:0px; padding:4px; word-break:break-all; max-width:100%; word-wrap:break-word!important; background-color:rgb(190,192,191)"><p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; max-width:100%; clear:both; min-height:1em; white-space:pre-wrap; word-wrap:break-word!important"><span style="margin:0px; padding:0px; max-width:100%; word-wrap:break-word!important">自主博客</span></p></td><td valign="top" width="111" height="13" style="border-style:solid; border-color:rgb(0,0,0); margin:0px; padding:4px; word-break:break-all; max-width:100%; word-wrap:break-word!important; background-color:rgb(190,192,191)"><p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; max-width:100%; clear:both; min-height:1em; white-space:pre-wrap; word-wrap:break-word!important"><span style="margin:0px; padding:0px; max-width:100%; word-wrap:break-word!important">微信</span></p></td><td valign="top" width="112" height="13" style="border-style:solid; border-color:rgb(0,0,0); margin:0px; padding:4px; word-break:break-all; max-width:100%; word-wrap:break-word!important; background-color:rgb(190,192,191)"><p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; max-width:100%; clear:both; min-height:1em; white-space:pre-wrap; word-wrap:break-word!important"><span style="margin:0px; padding:0px; max-width:100%; font-family:Helvetica; letter-spacing:0px; word-wrap:break-word!important">CSDN</span></p></td></tr><tr style="margin:0px; padding:0px; max-width:100%; word-wrap:break-word!important"><td valign="top" width="111" height="39" style="border-style:solid; border-color:rgb(0,0,0); margin:0px; padding:4px; word-break:break-all; max-width:100%; word-wrap:break-word!important; background-color:rgb(227,228,228)"><p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; max-width:100%; clear:both; min-height:1em; white-space:pre-wrap; word-wrap:break-word!important"><span style="margin:0px; padding:0px; max-width:100%; word-wrap:break-word!important">天地會珠海分舵</span></p></td><td valign="top" width="112" height="39" style="border-style:solid; border-color:rgb(0,0,0); margin:0px; padding:4px; word-break:break-all; max-width:100%; word-wrap:break-word!important"><p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; max-width:100%; clear:both; min-height:1em; white-space:pre-wrap; word-wrap:break-word!important"><span style="margin:0px; padding:0px; max-width:100%; font-size:11px; font-family:Helvetica; letter-spacing:0px; word-wrap:break-word!important"><a target="_blank" href="http://techgogogo.com/">http://techgogogo.com</a></span><span style="margin:0px; padding:0px; max-width:100%; font-family:Helvetica; font-size:11px; letter-spacing:0px; word-wrap:break-word!important"/></p><p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; max-width:100%; clear:both; min-height:14px; white-space:pre-wrap; font-family:Helvetica; word-wrap:break-word!important"><br style="margin:0px; padding:0px; max-width:100%; word-wrap:break-word!important"/></p></td><td valign="top" width="111" height="39" style="border-style:solid; border-color:rgb(0,0,0); margin:0px; padding:4px; word-break:break-all; max-width:100%; word-wrap:break-word!important"><p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; max-width:100%; clear:both; min-height:1em; white-space:pre-wrap; word-wrap:break-word!important"><span style="margin:0px; padding:0px; max-width:100%; word-wrap:break-word!important">服務號</span><span style="margin:0px; padding:0px; max-width:100%; font-size:10px; font-family:Helvetica; letter-spacing:0px; word-wrap:break-word!important">:TechGoGoGo</span></p><p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; max-width:100%; clear:both; min-height:1em; white-space:pre-wrap; word-wrap:break-word!important"><span style="margin:0px; padding:0px; max-width:100%; word-wrap:break-word!important">掃描碼</span><span style="margin:0px; padding:0px; max-width:100%; font-size:10px; font-family:Helvetica; letter-spacing:0px; word-wrap:break-word!important">:</span></p><p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; max-width:100%; clear:both; min-height:14px; white-space:pre-wrap; font-family:Helvetica; word-wrap:break-word!important"><img src="image/47cf4f9ec59b0ef1f807a6c33ab5ce5f.jpg" alt="" style="max-width:100%; margin:0px; padding:0px; height:auto!important; word-wrap:break-word!important; width:auto!important; visibility:visible!important"/></p></td><td valign="top" width="112" height="39" style="border-style:solid; border-color:rgb(0,0,0); margin:0px; padding:4px; word-break:break-all; max-width:100%; word-wrap:break-word!important"><p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; max-width:100%; clear:both; min-height:1em; white-space:pre-wrap; color:rgb(62,62,62); font-family:'Helvetica Neue',Helvetica,'Hiragino Sans GB','Microsoft YaHei',?¢èí??oú,Arial,sans-serif; font-size:18px; line-height:28.7999992370605px; word-wrap:break-word!important"><span style="margin:0px; padding:0px; max-width:100%; color:rgb(0,0,0); font-size:11px; font-family:Helvetica; letter-spacing:0px; word-wrap:break-word!important"><a target="_blank" href="http://blog.csdn.net/zhubaitian">http://blog.csdn.net/zhubaitian</a></span><span style="margin:0px; padding:0px; max-width:100%; color:rgb(0,0,0); font-family:Helvetica; font-size:11px; letter-spacing:0px; line-height:28.7999992370605px; word-wrap:break-word!important"/></p><div><span style="margin:0px; padding:0px; max-width:100%; color:rgb(0,0,0); font-family:Helvetica; font-size:11px; letter-spacing:0px; line-height:28.7999992370605px; word-wrap:break-word!important"><br/></span></div></td></tr></tbody></table>
- 前言
- MonkeyRunner創建一個Note的實例
- MonkeyRunner在Windows下的Eclipse開發環境搭建步驟(兼解決網上Jython配置出錯的問題)
- MonkenRunner通過HierarchyViewer定位控件的方法和建議(Appium/UIAutomator/Robotium姊妹篇)
- MonkeyDevcie API 實踐全記錄
- MonkeyImage API 實踐全記錄
- EasyMonkeyDevice vs MonkeyDevice&amp;HierarchyViewer API Mapping Matrix
- adb概覽及協議參考
- MonkeyRunner源碼分析之-誰動了我的截圖?
- MonkeyRunner源碼分析之與Android設備通訊方式
- MonkeyRunner源碼分析之啟動
- Monkey源碼分析之運行流程
- Monkey源碼分析之事件源
- Monkey源碼分析番外篇之WindowManager注入事件如何跳出進程間安全限制
- Monkey源碼分析番外篇之Android注入事件的三種方法比較
- Monkey源碼分析之事件注入
- monkey源碼分析之事件注入方法變化
- MonkeyRunner源碼分析之工作原理圖
- Android自動化測試框架新書:&lt;&lt;MonnkeyRunner實現原理剖析&gt;&gt;交流