在《MonkeyRunner源碼分析之與Android設(shè)備通訊方式》中,我們談及到MonkeyRunner控制目標(biāo)android設(shè)備有多種方法,其中之一就是在目標(biāo)機(jī)器啟動一個monkey服務(wù)來監(jiān)聽指定的一個端口,然后monkeyrunner再連接上這個端口來發(fā)送命令,驅(qū)動monkey去完成相應(yīng)的工作。
創(chuàng)新互聯(lián)主要從事網(wǎng)站建設(shè)、網(wǎng)站制作、網(wǎng)頁設(shè)計、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)吉利,10余年網(wǎng)站建設(shè)經(jīng)驗,價格優(yōu)惠、服務(wù)專業(yè),歡迎來電咨詢建站服務(wù):18982081108
當(dāng)時我們只分析了monkeyrunner這個客戶端的代碼是怎么實現(xiàn)這一點的,但沒有談monkey那邊是如何接受命令,接受到命令又是如何處理的。
所以自己打開源碼看了一個晚上,大概有了概念。但今天網(wǎng)上搜索了下,發(fā)現(xiàn)已經(jīng)有網(wǎng)友“chenjie”對monkey的源碼做過相應(yīng)的分析了,而且文章寫得非常有概括性,應(yīng)該是高手所為,果斷花了2個積分下載下來,不敢獨享,本想貼上來分享給大家,但發(fā)現(xiàn)pdf的文檔直接拷貝上來會丟失掉圖片,所以只好貼上下載地址:http://download.csdn.net/download/zqilu/6884491
但文章主要是架構(gòu)性得去描述monkey是怎么工作的,按照我自己的習(xí)慣,我還是喜歡按照自己的思維和有目的性的去了解我想要的,在這里我想要的是搞清楚monkey是如何處理monkeyrunner過來的命令的。
本文我們就先看下monkey的運(yùn)行流程。
base=/system export CLASSPATH=$base/framework/monkey.jar trap "" HUP exec app_process $base/bin com.android.commands.monkey.Monkey $*android中可以通過多種方式啟動java應(yīng)用,通過app_process命令啟動就是其中一種,它可以幫忙注冊android JNI,而繞過dalvik以使用Native API(如我般不清楚的請百度)所做的主要事情如下:
/** * 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); }
private int run(String[] args) { ... if (!processOptions()) { return -1; } ... }進(jìn)去之后就是很普通的讀取命令行的參數(shù)然后一個個進(jìn)行解析保存了,沒有太多特別的東西,這里就直接貼出monkey的參數(shù)選項大家看看就好了:
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(); ... }事件源代表測試數(shù)據(jù)的事件是從哪里過來的,不同的event source會有不同的類來做相應(yīng)的實現(xiàn):
private int run(String[] args) { ... int crashedAtCycle = runMonkeyCycles(); ... }如前所述,runMonkeyCyles方法會根據(jù)不同的數(shù)據(jù)源開始一條條的獲取事件并進(jìn)行執(zhí)行:
/** * 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就是我們上面提到的事件源的接口,它屏蔽了每個事件實現(xiàn)類的具體細(xì)節(jié),我們只需要告訴這個接口我們現(xiàn)在需要取一條事件然后執(zhí)行它,該結(jié)構(gòu)根據(jù)面向?qū)ο蟮亩鄳B(tài)原理,就會自動取事件的實現(xiàn)類獲得對應(yīng)的事件進(jìn)行返回。