1. 程式人生 > >第8章2節《MonkeyRunner源代碼剖析》MonkeyRunner啟動執行過程-解析處理命令行參數

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

path 轉載 iss 命令 code rst pri bsp ack

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)勾搭。

本文由天地會珠海分舵原創。轉載請自覺,是否投訴維權看心情。


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