天天看點

Appium Android Bootstrap源碼分析之指令解析執行

  下面我們還是先看一下從pc端發過來的json的格式是怎麼樣的:

Appium Android Bootstrap源碼分析之指令解析執行

  可以看到裡面除了params指定的是哪一個控件之外,還指定了另外兩個資訊:

  cmd: 這是一個action還是一個shutdown

  action:如果是一個action的話,那麼是什麼action

  開始前我們先簡要描述下我們需要涉及到幾個關鍵類:

Appium Android Bootstrap源碼分析之指令解析執行

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">1. appium指令解析器androidcommand</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">  androidcommand這個類真實的作用其實就是去把appium從pc端發送過來的那串json指令解析出來,它擁有兩個成員變量:</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">  jsonobject         json;</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">  androidcommandtype cmdtype;</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">  json就是pc過來的json格式的那串指令,cmdtype就是action或者shutdown,其實就是用來把這個類僞裝成更像個指令類而已,我認為如果不提供這個成員變量而直接修改其gettype的實作去解析json字串直接獲得對應的androidcommandtype,然後把這個類的名字改成androidcommandparser得了。</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">  那麼我們往下看下androidcommand究竟是怎麼對用戶端指令進行解析的,它的方法都很短,是以我把它做成一個表,這樣比較清晰點:</a>

Appium Android Bootstrap源碼分析之指令解析執行

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">  從表中的這些方法可以看出來,這個類所做的事情基本上都是怎麼去解析appium從pc端過來的那串json字串。</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">  2. action與commandhandler的映射關系</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">  從上面描述可以知道,一個action就是一個代表該指令的字串,比如‘click’。但是一個字串是不能去執行的啊,是以我們需要有一種方式把它轉換成可以執行的代碼,這個就是androidcommandexecutor維護的一個靜态hashmap map所做的事情:</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">class androidcommandexecutor {</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">private static hashmap&lt;string, commandhandler&gt; map = new hashmap&lt;string, commandhandler&gt;();</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">static {</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">map.put("waitforidle", new waitforidle());</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">map.put("clear", new clear());</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">map.put("orientation", new orientation());</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">map.put("swipe", new swipe());</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">map.put("flick", new flick());</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">map.put("drag", new drag());</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">map.put("pinch", new pinch());</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">map.put("click", new click());</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">map.put("touchlongclick", new touchlongclick());</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">map.put("touchdown", new touchdown());</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">map.put("touchup", new touchup());</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">map.put("touchmove", new touchmove());</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">map.put("gettext", new gettext());</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">map.put("settext", new settext());</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">map.put("getname", new getname());</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">map.put("getattribute", new getattribute());</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">map.put("getdevicesize", new getdevicesize());</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">map.put("scrollto", new scrollto());</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">map.put("find", new find());</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">map.put("getlocation", new getlocation());</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">map.put("getsize", new getsize());</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">map.put("wake", new wake());</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">map.put("pressback", new pressback());</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">map.put("presskeycode", new presskeycode());</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">map.put("longpresskeycode", new longpresskeycode());</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">map.put("takescreenshot", new takescreenshot());</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">map.put("updatestrings", new updatestrings());</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">map.put("getdatadir", new getdatadir());</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">map.put("performmultipointergesture", new multipointergesture());</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">map.put("opennotification", new opennotification());</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">map.put("source", new source());</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">map.put("compressedlayouthierarchy", new compressedlayouthierarchy());</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">}</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">  這個map指定了我們支援的pc端過來的所有action,以及對應的處理該action的類的執行個體,其實這些類都是commandhandler的子類基本上就隻有一個:去實作commandhandler的虛拟方法execute!要做的事情就大概就這幾類:</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">  控件相關的action:調用androidelement控件的成員變量uiobject el對應的方法來執行真實的操作</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">  uidevice相關的action:調用uidevice提供的方法</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">  uiscrollable相關的action:調用uiscrollable提供的方法</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">  uiautomator那5個對象都沒有的action:該調用interactioncontroller的就反射調用,該調用querycontroller的就反射調用。注意這兩個類uiautomator是沒有提供直接調用的方法的,是以隻能通過反射。更多這兩個類的資訊請翻看之前的uiautomator源碼分析相關的文章</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">  其他:如取得compressedlayouthierarchy</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">  指導action向commandhandler真正發生轉換的地方是在這個androidcommandexecutor的execute方法中:</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">public androidcommandresult execute(final androidcommand command) {</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">try {</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">logger.debug("got command action: " + command.action());</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">if (map.containskey(command.action())) {</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">return map.get(command.action()).execute(command);</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">} else {</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">return new androidcommandresult(wdstatus.unknown_command,</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">"unknown command: " + command.action());</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">} catch (final jsonexception e) {</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">logger.error("could not decode action/params of command");</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">return new androidcommandresult(wdstatus.json_decoder_error,</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">"could not decode action/params of command, please check format!");</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">  它首先叫上面的androidcommand解析器把json字串的action給解析出來</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">  然後通過剛提到的map把這個action對應的commandhandler的實作類給執行個體化</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">  然後調用這個指令處理類的execute方法開始執行指令</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">  3. 指令處理示例</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">  我們這裡就示例性的看下gettext這個action對應的commandhandler是怎麼去通過androidelement控件進行設定文本的處理的:</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">public class gettext extends commandhandler {</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">/*</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">* @param command the {@link androidcommand} used for this handler.</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">*</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">* @return {@link androidcommandresult}</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">* @throws jsonexception</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">* @see io.appium.android.bootstrap.commandhandler#execute(io.appium.android.</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">* bootstrap.androidcommand)</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">*/</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">@override</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">public androidcommandresult execute(final androidcommand command)</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">throws jsonexception {</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">if (command.iselementcommand()) {</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">// only makes sense on an element</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">final androidelement el = command.getelement();</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">return getsuccessresult(el.gettext());</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">} catch (final uiobjectnotfoundexception e) {</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">return new androidcommandresult(wdstatus.no_such_element,</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">e.getmessage());</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">} catch (final exception e) { // handle nullpointerexception</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">return geterrorresult("unknown error");</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">return geterrorresult("unable to get text without an element.");</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">  關鍵代碼就是裡面通過androidcommand的getelement方法:</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">  解析傳進來的androidcommand執行個體儲存的pc端過來的json字串,找到’params‘項的子項’elementid'</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">  通過這個獲得的id去控件哈希表(請檢視《appium android bootstrap源碼分析之控件androidelement》)中找到目标androidelement控件對象</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">  然後調用獲得的androidelement控件對象的gettext方法:</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">  最終通過調用androidelement控件成員uiobject控件對象的gettext方法取得控件文本資訊</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">  4. 小結</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">  bootstrap接收到appium從pc端發送過來的json格式的鍵值對字串有多個項:</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">  cmd: 這是一個action還是一個shutdown</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">  action:如果是一個action的話,那麼是什麼action,比如click</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">  params:擁有其他的一些子項,比如指定操作控件在androidelementhash維護的控件哈希表的控件鍵值的'elementid'</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">  在收到這個json格式指令字串後:</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">  androidcommandexecutor會調用androidcommand去解析出對應的action</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">  然後把action去map到對應的真實指令處理方法commandhandler的實作子類對象中</a>

<a href="http://www.51testing.com/batch.download.php?aid=49983" target="_blank">  然後調用對應的對象的execute方法來執行指令</a>

最新内容請見作者的github頁:http://qaseven.github.io/