天天看點

第8章2節《MonkeyRunner源代碼剖析》MonkeyRunner啟動執行過程-解析處理指令行參數

MonkeyRunnerStarter是MonkeyRunner啟動時的入口類,由于它裡面包括了main方法.它的整個啟動過程主要做了以下幾件事情:

  • 解析使用者啟動MonkeyRunner時從指令行傳輸進來的參數: 由于MonkeyRunner須要依據指定的參數才幹做事情,比方輸入的一個須要執行的腳本。

    假設确實不知道不論什麼參數的話它就會進入MonkeyRunner的互動模式,事實上就是Jythong的互動模式,讓使用者能夠邊寫代碼邊執行

  • 啟動AndroidDebugBridge: 事實上就是啟動ADBserver,由于MonkeyRunner跟裝置通信的一個很重要的方法之中的一個就是通過向ADBserver發送指令來請求目标裝置的服務
  • 啟動裝置監控線程: 事實上這個是在啟動AndroidDebugBridge的時候一并啟動的。裝置監控線程主要做的事情就是取監控裝置是否有接入進來或者移除出去,假設有新的裝置連接配接進來,或者說裝置變成ONLINE狀态(一個裝置有多個狀态:ONLINE|OFFLINE|RECOVERY|UNAUTHORIZED),那麼就須要取監控裝置裡面的每一個可調試程序,這主要是用來給DDMS等調試工具使用的。

    它維護了一個最新的裝置清單

  • 啟動AndroidDebugBridge:
  • 啟動Monkey:
  • 執行測試腳本:

本小節我們會先去看下MonkeyRunner在啟動的時候是怎樣獲得指令行參數并對其進行解析處理的。

整個過程事實上跟monkey在啟動的時候的指令行參數分析相似。往下我們先看下牽涉到的關鍵類之間的關系:

圖8-2-1  MonkeyRunnerStarter類圖

從類圖中我們看到MonkeyRunnerStarter持有了一個MonkeyRunnerOptions類型的成員變量options,這個執行個體儲存的就是解析出來的指令行參數。同一時候該類會提供一個processOptions方法來專門解析指令行參數。

我們先進入到MonkeyRunnerStart這個類的main方法:

178   public static void main(String[] args) {
179     MonkeyRunnerOptions options = 
MonkeyRunnerOptions.processOptions(args);
180 
181     if (options == null) {
182       return;
183     }
184 
185 
186     replaceAllLogFormatters(MonkeyFormatter.DEFAULT_INSTANCE, 
options.getLogLevel());
187 
188     MonkeyRunnerStarter runner = 
new MonkeyRunnerStarter(options);
189     int error = runner.run();
190 
191 
192     System.exit(error);
193   }
194 }      

代碼3-2-1 MonkeyRunnerStart - main

這裡主要做了三件事情:

  • 179行去處理使用者啟動monkeyrunner的時候輸入的指令行參數
  • 188行去初始化MonkeyRunnerStarter,裡面主要是初始化了ChimpChat。ChimpChat又去開啟AndroidDebugBridge程序和開啟DeviceMonitor裝置監控線程,我們往後小節會進行具體分析
  • 189行去把monkeyrunner執行起來,包括帶腳本參數的情況和不待腳本參數直接提供jython指令行的情況

我們這一節會先去分析下monkeyrunner是怎樣對參數進行處理的,我們跳轉到MonkeyRunnerOptions這個類裡面的processOptions這種方法:

93   public static MonkeyRunnerOptions processOptions(String[] args)
 94   {
 95     int index = 0;
 96 
 97     String hostname = DEFAULT_MONKEY_SERVER_ADDRESS;
 98     File scriptFile = null;
 99     int port = DEFAULT_MONKEY_PORT;
100     String backend = "adb";
101     Level logLevel = Level.SEVERE;
102 
103     ImmutableList.Builder<File> pluginListBuilder = ImmutableList.builder();
104     ImmutableList.Builder<String> argumentBuilder = ImmutableList.builder();
105     while (index < args.length) {
106       String argument = args[(index++)];
107 
108       if ("-s".equals(argument)) {
109         if (index == args.length) {
110           printUsage("Missing Server after -s");
111           return null;
112         }
113         hostname = args[(index++)];
114       }
115       else if ("-p".equals(argument))
116       {
117         if (index == args.length) {
118           printUsage("Missing Server port after -p");
119           return null;
120         }
121         port = Integer.parseInt(args[(index++)]);
122       }
123       else if ("-v".equals(argument))
124       {
125         if (index == args.length) {
126           printUsage("Missing Log Level after -v");
127           return null;
128         }
129 
130         logLevel = Level.parse(args[(index++)]);
131       } else if ("-be".equals(argument))
132       {
133         if (index == args.length) {
134           printUsage("Missing backend name after -be");
135           return null;
136         }
137         backend = args[(index++)];
138       } else if ("-plugin".equals(argument))
139       {
140         if (index == args.length) {
141           printUsage("Missing plugin path after -plugin");
142           return null;
143         }
144         File plugin = new File(args[(index++)]);
145         if (!plugin.exists()) {
146           printUsage("Plugin file doesn't exist");
147           return null;
148         }
149 
150         if (!plugin.canRead()) {
151           printUsage("Can't read plugin file");
152           return null;
153         }
154 
155         pluginListBuilder.add(plugin);
156       } else if (!"-u".equals(argument))
157       {
158         if ((argument.startsWith("-")) && (scriptFile == null))
159         {
160 
161 
162           printUsage("Unrecognized argument: " + argument + ".");
163           return null;
164         }
165         if (scriptFile == null)
166         {
167 
168           scriptFile = new File(argument);
169           if (!scriptFile.exists()) {
170             printUsage("Can't open specified script file");
171             return null;
172           }
173           if (!scriptFile.canRead()) {
174             printUsage("Can't open specified script file");
175             return null;
176           }
177         } else {
178           argumentBuilder.add(argument);
179         }
180       }
181     }
182 
183     return new MonkeyRunnerOptions(hostname, 
port, 
scriptFile, 
backend, 
logLevel, 
pluginListBuilder.build(), 
argumentBuilder.build());
184   }
185 }      

代碼8-2-2 MonkeyRunnerOptions  - processOptions

這裡首先請看99-101行的幾個變量初始化,假設使用者在指令行中沒有指定相應的參數,那麼這些預設參數就會被使用,我們且看下這些預設值各自是什麼:

  • hostname:相應‘-s'參數。預設值是'127.0.0.1',也就是本機。将會forward給目标裝置執行的monkey。是以加上以下的轉發port等同于目标機器在listen的monkey服務
  • port :相應‘-p'參數。預設值是'12345',也就是monkey預設監聽端口
  • backend :相應'-be'參數,預設值是‘adb‘,事實上往後看代碼我們會發現它也僅僅是支援’adb‘而已。

    這裡須要注意的是這是一個隐藏參數。指令行的help沒有顯示該參數

  • logLevel :相應‘-v'參數。預設值'SEVERE',也就是說僅僅列印嚴重的log

代碼往下就是對使用者輸入的參數的解析并儲存了,這裡要注意幾個隐藏的參數:

  • -u :乍一看以為這是一個什麼特别的參數。從156-178行能夠看到這個參數處理的意義是:當使用者輸入'-u'的時候不會作不論什麼處理,但當使用者輸入的是由‘-’開始的但又不是monkeyrunner聲稱支援的那幾個參數的時候,就會依據不同的情況給使用者報錯。

    是以這段代碼的意思事實上就是在使用者輸入了不支援的參數的時候依據不同的情況給使用者提示而已

  • -be :backend。如前所述。僅僅支援‘adb'
  • -plugin :這裡須要一個背景知識,在google官網有說明,使用者能夠通過遵循一定的規範去編寫插件來擴充monkeyrunner的功能,比方在monkeydevice裡面按下這個動作是須要通過MonkeyDevice.DOWN這個參數來傳給press這種方法的。假設你認為這樣子不好,你希望添加個pressDown這種方法。裡面預設就是用MonkeyDevice.DOWN來驅動MonkeyDevice的press方法,而使用者僅僅須要給出坐标點就能夠了,那麼你就能夠遵循google描寫叙述的規範去編寫一個這方面的插件。到時使用的時候就能夠通過python方式直接import進來使用了。本書并不會把MonkeyRunner插件進行重點介紹。

在解析出全部的參數之後,processOptions方法最後依據這些參數來初始化MonkeyRunnerOptions類。

我們進入到該構造函數看下它到底做了什麼事情:

38   private MonkeyRunnerOptions(String hostname, int port, File scriptFile, String backend, Level logLevel, Collection<File> plugins, Collection<String> arguments)
 39   {
 40     this.hostname = hostname;
 41     this.port = port;
 42     this.scriptFile = scriptFile;
 43     this.backend = backend;
 44     this.logLevel = logLevel;
 45     this.plugins = plugins;
 46     this.arguments = arguments;
 47   }      

代碼8-2-3 MonkeyRunnerOptions - 構造函數

所做的事情很easy。就是把解析出來的全部參數儲存到MonkeyRunnerOptions類的執行個體裡面。今後須要的時候就進去拿就好了。

注:很多其它文章請關注公衆号:techgogogo或個人部落格http://techgogogo.com。當然。也很歡迎您直接微信(zhubaitian1)勾搭。