Android Monkey源碼解析


本文旨在對Android Monkey的源碼進行解析,這樣能在后續的定制改造中得心應手。 

對於此源碼,自己獲取的過程也是廢了一般周折,嘗試過去手機里反編譯,去各種地方找,后來發現,通過Google搜索“android monkey source code”,第一條就是,附上地址:https://github.com/android/platform_development/tree/master/cmds/monkey

所以有個感想,有時候得用英文搜,然后最好用Google搜。

一、使用

Monkey的使用很簡單,需要注意的是各個參數的意義要搞清楚。

這篇文章並不會講其使用,具體可以參見Google的官方文檔[1],或者一篇博客[2]。

 

二、源碼解析

1 ,閱讀代碼首先得理清楚主線,也即是執行流程。對應到Monkey中,也就是怎么通過在控制台中輸入一串命令,就可以得到相應的測試結果的。因為主類Monkey中有main方法存在,再調用run()方法,一路跟下去,便可以理清楚其主線。下圖是自己畫的一個UML順序圖。本小節先對主線做簡要介紹,后面兩小節重點解析MonkeyEventSource和MonkeyEvent。

 

 

  1)在run() 方法中,第一個比較重要的是參數處理方法processOptions(),用來解析命令行傳入的參數,設置相應的變量。

  2)然后會初始化EventSource,主要是最后一種,MonkeySourceRandom。這時會把通過命令行讀入的參數全部設置到對應的變量中。在通過調用validate()方法,將傳入的參數結合默認的參數進行調整和校驗。

  3)再調用runMonkeyCycles()方法執行具體事件。通過EventSource調用getNextEvent()獲取到MonkeyEvent,對於具體的事件則調用injectEvent()觸發真正的執行。對於返回的結果做相應的處理。

  4)處理好后,在打印相應的日志結果即可。

  5)對於其中的ActivityController,由於繼承的是內部隱藏的類,可不比關注。

 

2,對於MonkeyEventSource,它是一個接口,主要的實現類是MonkeySourceRandom,也就是默認的隨機事件,當然也還有網絡,腳本兩種事件源,其繼承關系如下圖。

(這是一種典型的面向對象的編程范式)

 

  1)由於基於網絡的事件只能作用於localhost,而基於腳本的事件其腳本並不友好,因此一般就用MonkeySourceRandom這個事件源,也就是默認的通過命令行觸發的事件。下面就分析下MonkeySourceRandom這個類。

  2)該類首先是定義了10種事件,分別是TOUCH, MOTION, PINCHZOOM, TRACKBALL, NAV, MAJORNAV, SYSOPS, APPSWITCH, FLIP和ANYTHING,這些事件和命令行中的可設置10種參數對應。

  在構造函數中設置了各個事件的初始占比,並定了一個用於存儲事件的隊列。並定義了3種手勢,TAP,DRAG, PINCH_OR_ZOOM。

  3)然后,提供接口setFactors()給Monkey類對各個參數進行設置。再通過接口adjustEventFactors(),結合命令行輸入和默認值,調整10個事件的比例,進行歸一化。

  4)該類中關鍵的作用是用於生成隨機事件,通過generateEvents()這個私有方法完成。

  對於TOUCH, MOTION, PINCHZOOM這三種,分別對應於TAP,DRAG和 PINCH_OR_ZOOM三種手勢,利用generatePointerEvent()方法完成;對於TRACKBALL,利用方法generateTrackballEvent()完成 。

  在generatePointerEvent()方法里,實際生成的都是MonkeyTouchEvent。如果傳入的手勢是TAP,則隨機按下一個點,移動一次,再放開;如果是DRAG,則按下后,會隨機移動一定數量的點后再放開;如果是PINCH_OR_ZOOM,由於是按下的是兩個點,各自的id分別為0和1,然后隨機一定數量的點再放開。

  在generateTrackballEvent()方法里,生成的是MonkeyTrackballEvent。首先是隨機的移動10次,然后還有10%的概率在移動完進行一次點擊。

  對於APPSWITCH,生成一個MonkeyActivityEvent事件;對於FLIP,生成一個MonkeyFlipEvent事件;而剩下的NAV, MAJORNAV, SYSOPS,都從各自的可選點中隨機選出一個,對於ANYTHING則隨機生成一個,這四種最后都生成MonkeyKeyEvent事件。

  由此可以總結出,命令行中可配置的10種事件類型,裝換為了Monkey中的5種Event,其對應關系如下表:

命令行可選參數 FACTOR MonkeyEvent類型
--pct-touch FACTOR_TOUCH MonkeyTouchEvent
--pct-motion FACTOR_MOTION
--pct-pinchzoom FACTOR_PINCHZOOM
--pct-trackball FACTOR_TRACKBALL MonkeyTrackballEvent
--pct-syskeys FACTOR_SYSOPS MonkeyKeyEvent
--pct-nav FACTOR_NAV
--pct-majornav FACTOR_MAJORNAV
--pct-anyevent FACTOR_ANYTHING
--pct-appswitch FACTOR_APPSWITCH MonkeyActivityEvent
--pct-flip FACTOR_FLIP MonkeyFlipEvent

 

  5)該類通過getNextEvent()方法,供主類Monkey調用。所有事件放在MonkeyEventQueue中維護,如果為空則創建,不為空則返回第一個。

 

3,對於MonkeyEvent,有多種,也都是繼承了基類MonkeyEvent,其關系如下圖。(這同樣是一種典型的面向對象的編程范式)

 

  1)對於基類MonkeyEvent,定義了七種事件類型,都以EVENT_TYPE_開頭,有KEY, TOUCH, TRACKBALL, ACTIVITY, FLIP, THROTTLE, NOOP。分別對應上面的各個葉子類(Motion派生出了Touch和Trackball)。在各個子類的構造函數中,都會根據類型調用基類的構造函數。當然需要注意的是,對於ACTIVITY這個類型,除了MonkeyActivityEvent,還有MonkeyCommandEvent, MonkeyGetFrameRateEvent, MonkeyInstrumentationEvent, MonkeyPowerEvent這四種事件也對應該類型。(具體原因還有待分析)

  2)基類MonkeyEvent中還定了了四種執行結果的返回碼,分別是SUCESS, FAIL, REMOTE_EXCEPTION, SECURITY_EXCEPTION。在各個派生類都要覆寫的抽象方法injectEvent()中,都需要按情況返回對應的某種結果編碼。

  3)下面重點看下MonkeyMotionEvent(以及其派生出的MonkeyTouchEvent和MonkeyTrackBallEvent)。在設計上,采用了構建者(Builder)設計模式,可以讓MonkeySource方便的構建出所需的特定事件。在內部事件處理中,采用Android的view組件中的MotionEvent,具體執行時,將MotionEvent對象傳給派生類TouchEvent和TrackBallEvent處理,它們分別調用系統內部IWindowManager,的接口執行對應的Touch和TrackBall事件。需要注意的事,MotionEvent執行完最好進行recycle()。

  4)對於MonkeyKeyEvent,采用了和MonkeyMotionEvent類似的思路,采用Android的view組件中的KeyEvent,具體事件執行也是利用IWindowManager調用。而對於MonkeyActivityEvent,也類似,采用了Android的content組件中的Intent,利用IActivityManager執行具體事件。至於MonkeyThrottleEvent,就是休眠一段時間;而MonkeyNoopEvent,就什么都不干。

  對於MonkeyCommandEvent, MonkeyGetFrameRateEvent, MonkeyInstrumentationEvent, MonkeyPowerEvent,主要用在腳本執行(MonkeySourceScript)中,現實中很少用到。

  5)通過對各個具體Event的分析,主要是在方法injectEvent()中通過特定的方式去執行相應的事件,有的簡單,有的利用了系統的接口。如果后面需要改造這個代碼,讓其在用在自己的項目中,那就必須得去掉這些系統的內部接口,改用其他的方式。

 

4,再看下MonkeyEventQueue這個類,它繼承自LinkedList,用來維護Monkey Event。

(在MonkeySourceRandom中,產生的事件都放在MonkeyEventQueue定義的mQ中;當在Monkey中需要獲取一個事件時,也通過mQ的getFirst來獲取。)

重點看下MonkeyEventQueue覆寫的LinedList的addLast方法,其主要是對有間隔時間的事件做特殊處理,如果設置間隔時間隨機則還要取一個隨機數。

 1  public void addLast(MonkeyEvent e) {  2         super.add(e);  3         if (e.isThrottlable()) {  4             long throttle = mThrottle;  5             if (mRandomizeThrottle && (mThrottle > 0)) {  6                 throttle = mRandom.nextLong();  7                 if (throttle < 0) {  8                     throttle = -throttle;  9  } 10                 throttle %= mThrottle; 11                 ++throttle; 12  } 13             super.add(new MonkeyThrottleEvent(throttle)); 14  } 15     }

 

三、代碼的改造

  通過以上的分析,發現可改造的一點是,在具體事件的執行上,去掉對IWindowManager和IActivityManager的依賴。

 

(暫時先寫到這邊)

 

 

參考:

[1] Google關於monkey的官方文檔:http://developer.android.com/tools/help/monkey.html

[2] 博客:http://www.cnblogs.com/by-dream/p/5157308.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM