【Android測試】【第七節】Monkey——源碼淺談

【Android測試】【第七節】Monkey——源碼淺談

 版權聲明:本文出自胖喵~的博客,轉載必須註明出處。

    轉載請註明出處:http://www.cnblogs.com/by-dream/p/4713466.html

 

 

前言


   根據上一篇我們學會了Monkey的用法,知道了Monkey可以非常容易的模擬僞隨機的模擬事件。也許有的時候我們想讓他稍微智能化一些,例如只在某個屏幕範圍產生僞隨機事件,或者說是只對某些指定Activity進行操作,這樣就需要我們對Monkey進行改良了。而改良必須去改Monkey的源碼,因此本節課們就簡單的說說Monkey的源碼。

  源碼下載地址:https://code.google.com/p/android-source-browsing/source/browse/cmds/monkey/src/com/android/commands/monkey/?repo=platform--development&name=android-cts-4.2_r2  ( 這裏只是Monkey的源碼,如果要編譯Monkey需要下載Android的源碼 )

  

概述


  如果你真的打算改造一個屬於你的Monkey,那麼過程必須要做的是:

  1、下載Android源碼

  2、閱讀Monkey源碼如果需要修改代碼

  3、代碼編譯

  4、運行Monkey

  本節內容主要針對第二部分的 “閱讀Monkey源碼”,其他的1、3、4 部分會在另外一篇“只允許註冊用戶訪問的”的番外篇裏進行介紹,因爲這部分有些內容不是本人原創,因此對博客進行了加密處理,以免侵犯到源作者的權利,如需交流這部分內容,請留言給我。

 

Monkey源碼


      Monkey的入口在 Monkey.java中:

複製代碼
 /**
     * 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);
    }
複製代碼

  第一句的意思就是在 shell 命令行下 使用 ps | grep com.**.monkey 就找到正在運行的monkey進程

  第二句是後續的內容,我們繼續看後續幹了什麼。

 run

  這個run中的內容基本就是Monkey運行的流程,主要做了:

  1、處理命令行參數

if (!processOptions()) {
            return -1;
        }

    沒有什麼特殊的地方,主要是針對下面這張圖裏我們用到的參數進行一個統計和預處理。

  

  2、處理要拉起的應用程序的Activity:

    我們在運行Monkey的時候,如果指定了“ -p 包名 ”,那麼Monkey一定會拉起這個App的第一個Activity,這個究竟是怎麼實現的呢?就是藉助Intent這個東西:

     // now set up additional data in preparation for launch
        if (mMainCategories.size() == 0) {
            mMainCategories.add(Intent.CATEGORY_LAUNCHER);
            mMainCategories.add(Intent.CATEGORY_MONKEY);
        }

 

  3、處理Source模塊:

    Source模塊,以MonkeyEventSource爲接口,衍生出三種Source類:MonkeySourceRandom類(隨機生成事件)、MonkeySourceScript(從腳本獲取事件)、MonkeySourceNetwork(從網絡獲取事件)。    

複製代碼
      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();
        }
複製代碼

    這部分只要是來判斷Monkey的事件源來自何方,根據這些事件的來源,由不同的類做處理。MonkeySourceRandom事件的來源就是我們在命令行輸入參數後的僞隨機壓力測試;MonkeySourceScript事件來源於Monkey識別的一種腳本,事實上Monkey也可以做到通過腳本指定位置點擊,滑動等操作,但是該腳本的可讀性非常的差,編寫不易,因此這裏我也沒有介紹;第三種MonkeySourceNetwork來自於後面我們要講的Monkeyrunner,Monkeyrunner通過socket將一些要處理的事件發給Monkey,由Monkey來完成最後的處理。

 

  4、循環處理事件:

        mNetworkMonitor.start();
        int crashedAtCycle = runMonkeyCycles();
        mNetworkMonitor.stop();    

    主要看看 runMonkeyCycles() 這個函數主要做了什麼:

複製代碼
    /** 
     * 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);  
                ...  
             }  
        ...  
        }  
       ....  
}  
複製代碼

    這裏涉及到了一個重要的東西就是MonkeyEvent。

    以MonkeyEvent爲基類,衍生出各種Event類,如Monkey中常見的點擊,輸入,滑動事件;

    那麼一個點擊的操作究竟是怎麼進行下去的呢?我們可以到上面調用的是injectEvent,這個方法是由基類定義的,每一個子類去實現不同的內容,點擊、滑動等這個方法都是通過第一個參數一個iWindowManager的對象而完成的,當然也有不需要這個參數,例如MonkeyThrottleEvent這個類的實現方法,根本沒有用到iwm:

複製代碼
@Override  
public int injectEvent(IWindowManager iwm, IActivityManager iam, int verbose) {  
  
    if (verbose > 1) {  
        System.out.println("Sleeping for " + mThrottle + " milliseconds");  
    }  
    try {  
        Thread.sleep(mThrottle);  
    } catch (InterruptedException e1) {  
        System.out.println("** Monkey interrupted in sleep.");  
        return MonkeyEvent.INJECT_FAIL;  
    }  
      
    return MonkeyEvent.INJECT_SUCCESS;  
}  
複製代碼

    那麼這個iWindowManager的對象究竟是什麼呢?這個事系統隱藏的一個接口,通過這個接口可以注入一些操作事件,那麼我們以後是不是也可以用這個接口來進行事件的注入呢?答案是no,爲什麼呢?我們來看看:

    谷歌爲了方便Monkey能夠輕鬆的完成一些點擊、滑動事件,因此在使用了這個系統隱藏的接口,Monkey這個應用擁有這個兩個獨特的權限:第一個是SET_ACTIVITY_WATCHER這個權限,它允許monkey對activity的生命週期進行全權控制。第二個就是INJECT_EVENTS這個權限它允許monkey去模擬觸摸和按鍵事件。爲了防止這個系統隱藏接口暴露出的漏洞,普通的App是不能請求到這些權限的,只有android系統同意的應用纔會得到允許獲得這些權限。爲了防止壞人使用Monkey來進行這個事件的注入,Monkey也只被允許root運行或者是shell這個組的成員運行。

 

分類: 軟件測試
2
0
(請您對文章做出評價)
« 上一篇:【Android測試】【第六節】Monkey——認識和使用
» 下一篇:【Android測試】【第八節】Monkey——源碼改造
posted @ 2015-08-08 16:54 胖喵~ Views(522) Comments(17Edit 收藏

  
#1樓 2015-08-27 17:31 | smile花兒  
樓主寫的非常的好

  
#2樓[樓主2015-08-31 13:50 | 胖喵~  
@ smile花兒
謝謝支持 我會繼續的

  
#3樓 2016-03-09 15:31 | 瘋狂的壞丫頭  
第八節源碼改造博文的閱讀密碼是什麼?

  
#4樓[樓主2016-03-09 15:36 | 胖喵~  
@ 瘋狂的壞丫頭
看我給你的私信

  
#5樓 2016-04-22 16:29 | 宇智波臀  
Lz,第八節閱讀密碼是啥

  
#6樓[樓主2016-04-22 19:29 | 胖喵~  
@ 宇智波臀
私信你~

  
#7樓 2016-05-04 15:53 | double11  
Lz,第八節閱讀密碼是什麼?

  
#8樓 2016-05-15 00:16 | luceion  
樓主很贊,第八節到了關鍵的部分了,求私信下密碼~謝謝

  
#9樓[樓主2016-05-16 16:33 | 胖喵~  
@ luceion
私信你

  
#10樓 2016-05-17 09:45 | mars66  
很想看第八節的內容,求下私信密碼,感激不盡

  
#11樓[樓主2016-05-17 23:16 | 胖喵~  
@ mars66
已經私信你

  
#12樓 2016-05-26 14:08 | 酸酒鴨  
樓主看到了第八節內容,求密碼,謝謝

  
#13樓 2016-05-27 15:51 | 白開水echo  
求下一章密碼

  
#14樓[樓主2016-05-27 15:55 | 胖喵~  
@ 酸酒鴨
私信你了

  
#15樓[樓主2016-05-27 15:55 | 胖喵~  
@ 白開水echo
私信你了

  
#16樓 2016-05-27 17:10 | 白開水echo  
源碼哪裏下載?鏈接不可以

  
#17樓[樓主2016-05-29 17:43 | 胖喵~  
@ 白開水echo
可以下載的 得繞過牆

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章