溫馨提示×

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

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Monkey源碼分析之運(yùn)行流程

發(fā)布時(shí)間:2020-06-21 02:52:01 來(lái)源:網(wǎng)絡(luò) 閱讀:240 作者:zhukev 欄目:移動(dòng)開(kāi)發(fā)

在《MonkeyRunner源碼分析之與Android設(shè)備通訊方式》中,我們談及到MonkeyRunner控制目標(biāo)android設(shè)備有多種方法,其中之一就是在目標(biāo)機(jī)器啟動(dòng)一個(gè)monkey服務(wù)來(lái)監(jiān)聽(tīng)指定的一個(gè)端口,然后monkeyrunner再連接上這個(gè)端口來(lái)發(fā)送命令,驅(qū)動(dòng)monkey去完成相應(yīng)的工作。

當(dāng)時(shí)我們只分析了monkeyrunner這個(gè)客戶端的代碼是怎么實(shí)現(xiàn)這一點(diǎn)的,但沒(méi)有談monkey那邊是如何接受命令,接受到命令又是如何處理的。

所以自己打開(kāi)源碼看了一個(gè)晚上,大概有了概念。但今天網(wǎng)上搜索了下,發(fā)現(xiàn)已經(jīng)有網(wǎng)友“chenjie”對(duì)monkey的源碼做過(guò)相應(yīng)的分析了,而且文章寫(xiě)得非常有概括性,應(yīng)該是高手所為,果斷花了2個(gè)積分下載下來(lái),不敢獨(dú)享,本想貼上來(lái)分享給大家,但發(fā)現(xiàn)pdf的文檔直接拷貝上來(lái)會(huì)丟失掉圖片,所以只好貼上下載地址:http://download.csdn.net/download/zqilu/6884491

但文章主要是架構(gòu)性得去描述monkey是怎么工作的,按照我自己的習(xí)慣,我還是喜歡按照自己的思維和有目的性的去了解我想要的,在這里我想要的是搞清楚monkey是如何處理monkeyrunner過(guò)來(lái)的命令的。

本文我們就先看下monkey的運(yùn)行流程。

1. 運(yùn)行環(huán)境設(shè)置

和monkeyrunner一樣,monkey這個(gè)命令也是一個(gè)shell腳本,它是在我們的目標(biāo)android設(shè)備的“/system/bin/monkey”,其實(shí)這是一個(gè)android上面java程序啟動(dòng)的標(biāo)準(zhǔn)流程。

base=/system export CLASSPATH=$base/framework/monkey.jar trap "" HUP exec app_process $base/bin com.android.commands.monkey.Monkey $*
android中可以通過(guò)多種方式啟動(dòng)java應(yīng)用,通過(guò)app_process命令啟動(dòng)就是其中一種,它可以幫忙注冊(cè)android JNI,而繞過(guò)dalvik以使用Native API(如我般不清楚的請(qǐng)百度)所做的主要事情如下:

  • 設(shè)置monkey的CLASSPATH環(huán)境變量指向monkey.jar
  • 通過(guò)app_process指定monkey的入口和傳進(jìn)來(lái)的所有參數(shù)啟動(dòng)上面CLASSPATH設(shè)定的monkey.jar

2.命令行參數(shù)解析

通過(guò)以上的app_process指定的monkey入口,我們可以知道我們的入口函數(shù)main是在com.android.commands.Monkey這個(gè)類里面的:
    /**      * 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);     } 
入口函數(shù)很簡(jiǎn)單,直接跳到run這個(gè)方法,這是一個(gè)很重要的方法,里面大概會(huì)做以下這些事情:
  • 處理命令行參數(shù)
  • 根據(jù)命令行參數(shù)啟動(dòng)不同的事件源,也就是我們的測(cè)試事件究竟是從網(wǎng)絡(luò)如monkeyrunner過(guò)來(lái)的還是monkey內(nèi)部的random測(cè)試數(shù)據(jù)集過(guò)來(lái)的還是腳本過(guò)來(lái)的如此之類
  • 跳入runMonkeyCyncle方法針對(duì)不同的事件源開(kāi)始獲取并執(zhí)行不同的事件
這個(gè)方法會(huì)比較長(zhǎng),我們只看我們現(xiàn)在想要的關(guān)鍵片段,可以看到里面調(diào)用了命令行處理函數(shù)processOptions.
    private int run(String[] args) {     ...         if (!processOptions()) {             return -1;         }     ... }
進(jìn)去之后就是很普通的讀取命令行的參數(shù)然后一個(gè)個(gè)進(jìn)行解析保存了,沒(méi)有太多特別的東西,這里就直接貼出monkey的參數(shù)選項(xiàng)大家看看就好了:
Monkey源碼分析之運(yùn)行流程

3. 初始化測(cè)試事件源

如前所述,run方法里面在獲得命令行參數(shù)后會(huì)進(jìn)入下一個(gè)環(huán)節(jié),就是根據(jù)不同的參數(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();         ... }
事件源代表測(cè)試數(shù)據(jù)的事件是從哪里過(guò)來(lái)的,不同的event source會(huì)有不同的類來(lái)做相應(yīng)的實(shí)現(xiàn):
  • MonkeySourceNetwork.java: 事件是從網(wǎng)絡(luò)如monkeyrunner過(guò)來(lái)的,處理的是《MonkeyRunner源碼分析之與Android設(shè)備通訊方式》描述的界面控制操作事件
  • MonkeySourceNetworkVars.java: 事件也是從網(wǎng)絡(luò)如monkeyrunner過(guò)來(lái)的,處理的是《MonkeyRunner源碼分析之與Android設(shè)備通訊方式》提到的getPropery事件
  • MonkeySourceNetworkViews.java:事件也是從網(wǎng)絡(luò)如monkeyrunner過(guò)來(lái)的,處理的是《MonkeyRunner源碼分析之與Android設(shè)備通訊方式》提到的Views相關(guān)的事件
  • MonkeySourceRandom.java:事件是從monkey內(nèi)部生成的隨機(jī)事件集,也就是我們通過(guò)命令行啟動(dòng)monkey測(cè)試目標(biāo)app的常用方式
  • MonkeySourceRanodomeScript.java: 上面的隨機(jī)內(nèi)部數(shù)據(jù)源也可以通過(guò)指定setup腳本來(lái)創(chuàng)建
  • MonkeySourceScript.java: 用戶也可以遵循一定的規(guī)則編寫(xiě)monkey腳本來(lái)驅(qū)動(dòng)monkey進(jìn)行相關(guān)測(cè)試,與上面不同的是它不再是隨機(jī)的
往后的文章我們會(huì)針對(duì)其中一個(gè)事件源進(jìn)行分析,在這里我們只需要知道這些事件源代表了事件的不同的來(lái)源,它會(huì)
  • 從指定的源獲取命令
  • 把命令翻譯成monkey事件然后放到命令隊(duì)列EventQueue
這里需要注意的是每一個(gè)EventSource類都是實(shí)現(xiàn)了MonkeyEventSource這個(gè)接口的,這個(gè)接口最重要的就是要求實(shí)現(xiàn)類必須實(shí)現(xiàn)getNextEvent這個(gè)方法來(lái)生成并獲取事件。這樣子做的好處就是屏蔽了每一個(gè)具體事件源的實(shí)現(xiàn)細(xì)節(jié),其他地方的代碼只需要調(diào)用這個(gè)接口的getNextEvent方法獲得事件源就行了,而無(wú)需關(guān)心這些事件是從哪個(gè)源過(guò)來(lái)的。這些都是面向?qū)ο蟮拿嫦蚪涌诰幊痰幕A(chǔ)了,大家有不清楚的最好先去了解下java的一些基本知識(shí),這樣理解起來(lái)會(huì)快很多。

4. 循環(huán)執(zhí)行事件

run方法根據(jù)參數(shù)從不同的事件源獲得事件并放入到EventQueue后,就會(huì)開(kāi)始執(zhí)行一個(gè)循環(huán)去從EventQueue里獲取事件進(jìn)行執(zhí)行
    private int run(String[] args) {         ...         int crashedAtCycle = runMonkeyCycles();         ... }
如前所述,runMonkeyCyles方法會(huì)根據(jù)不同的數(shù)據(jù)源開(kāi)始一條條的獲取事件并進(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就是我們上面提到的事件源的接口,它屏蔽了每個(gè)事件實(shí)現(xiàn)類的具體細(xì)節(jié),我們只需要告訴這個(gè)接口我們現(xiàn)在需要取一條事件然后執(zhí)行它,該結(jié)構(gòu)根據(jù)面向?qū)ο蟮亩鄳B(tài)原理,就會(huì)自動(dòng)取事件的實(shí)現(xiàn)類獲得對(duì)應(yīng)的事件進(jìn)行返回。
所以這里大家還需要對(duì)多態(tài)這個(gè)概念有所了解,特別是一些從手動(dòng)測(cè)試轉(zhuǎn)到自動(dòng)化測(cè)試的朋友,可能之前沒(méi)有接觸過(guò)太多面向?qū)ο蟮闹R(shí)。本人以前做過(guò)開(kāi)發(fā),所以還ok。這里只是做一個(gè)善意的提醒。
獲得事件后下一步就是去執(zhí)行相應(yīng)的事件了,不同的事件會(huì)有不同的處理方式,或只是執(zhí)行個(gè)命令,或調(diào)用WindowManager隱藏接口做事件注入等,這些都會(huì)在今后文章進(jìn)行進(jìn)一步闡述

這一篇文章就到此為止了,目的就是讓大家對(duì)整一個(gè)monkey執(zhí)行的流程有個(gè)初步的了解,方便理解往下的相關(guān)文章。


<abbr id="kvxqe"><strong id="kvxqe"></strong></abbr>
 

作者

自主博客

微信

CSDN

天地會(huì)珠海分舵

http://techgogogo.com


服務(wù)號(hào):TechGoGoGo

掃描碼:

Monkey源碼分析之運(yùn)行流程

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI

<tt id="kvxqe"><optgroup id="kvxqe"></optgroup></tt>
  • <i id="kvxqe"></i>

    <pre id="kvxqe"><thead id="kvxqe"></thead></pre>
  • <bdo id="kvxqe"></bdo>
  • <bdo id="kvxqe"></bdo>