第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 }
這裏主要做了三件事情:
- 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)勾搭。
本文由天地會珠海分舵原創。轉載請自覺,是否投訴維權看心情。
第8章2節《MonkeyRunner源代碼剖析》MonkeyRunner啟動執行過程-解析處理命令行參數