Appium Android Bootstrap源碼分析之命令解析執行
通過上一篇文章《Appium Android Bootstrap源碼分析之控件AndroidElement》我們知道了Appium從pc端發送過來的命令如果是控件相關的話,最終目標控件在bootstrap中是以AndroidElement對象的方式呈現出來的,并且該控件對象會在AndroidElementHash維護的控件哈希表中保存起來。但是appium觸發一個命令除了需要提供是否與控件相關這個信息外,還需要其他的一些信息,比如,這個是什么命令?這個就是我們這篇文章需要討論的話題了。
下面我們還是先看一下從pc端發過來的json的格式是怎么樣的:
可以看到里面除了params指定的是哪一個控件之外,還指定了另外兩個信息:
cmd: 這是一個action還是一個shutdown
action:如果是一個action的話,那么是什么action
開始前我們先簡要描述下我們需要涉及到幾個關鍵類:

從表中的這些方法可以看出來,這個類所做的事情基本上都是怎么去解析appium從pc端過來的那串json字串。
2. Action與CommandHandler的映射關系
從上面描述可以知道,一個action就是一個代表該命令的字串,比如‘click’。但是一個字串是不能去執行的啊,所以我們需要有一種方式把它轉換成可以執行的代碼,這個就是AndroidCommandExecutor維護的一個靜態HashMap map所做的事情:
class AndroidCommandExecutor { private static HashMap<String, CommandHandler> map = new HashMap<String, CommandHandler>(); static { map.put("waitForIdle", new WaitForIdle()); map.put("clear", new Clear()); map.put("orientation", new Orientation()); map.put("swipe", new Swipe()); map.put("flick", new Flick()); map.put("drag", new Drag()); map.put("pinch", new Pinch()); map.put("click", new Click()); map.put("touchLongClick", new TouchLongClick()); map.put("touchDown", new TouchDown()); map.put("touchUp", new TouchUp()); map.put("touchMove", new TouchMove()); map.put("getText", new GetText()); map.put("setText", new SetText()); map.put("getName", new GetName()); map.put("getAttribute", new GetAttribute()); map.put("getDeviceSize", new GetDeviceSize()); map.put("scrollTo", new ScrollTo()); map.put("find", new Find()); map.put("getLocation", new GetLocation()); map.put("getSize", new GetSize()); map.put("wake", new Wake()); map.put("pressBack", new PressBack()); map.put("pressKeyCode", new PressKeyCode()); map.put("longPressKeyCode", new LongPressKeyCode()); map.put("takeScreenshot", new TakeScreenshot()); map.put("updateStrings", new UpdateStrings()); map.put("getDataDir", new GetDataDir()); map.put("performMultiPointerGesture", new MultiPointerGesture()); map.put("openNotification", new OpenNotification()); map.put("source", new Source()); map.put("compressedLayoutHierarchy", new CompressedLayoutHierarchy()); } |
這個map指定了我們支持的pc端過來的所有action,以及對應的處理該action的類的實例,其實這些類都是CommandHandler的子類基本上就只有一個:去實現CommandHandler的虛擬方法execute!要做的事情就大概就這幾類:
控件相關的action:調用AndroidElement控件的成員變量UiObject el對應的方法來執行真實的操作
UiDevice相關的action:調用UiDevice提供的方法
UiScrollable相關的action:調用UiScrollable提供的方法
UiAutomator那5個對象都沒有的action:該調用InteractionController的就反射調用,該調用QueryController的就反射調用。注意這兩個類UiAutomator是沒有提供直接調用的方法的,所以只能通過反射。更多這兩個類的信息請翻看之前的UiAutomator源碼分析相關的文章
其他:如取得compressedLayoutHierarchy
指導action向CommandHandler真正發生轉換的地方是在這個AndroidCommandExecutor的execute方法中:
public AndroidCommandResult execute(final AndroidCommand command) { try { Logger.debug("Got command action: " + command.action()); if (map.containsKey(command.action())) { return map.get(command.action()).execute(command); } else { return new AndroidCommandResult(WDStatus.UNKNOWN_COMMAND, "Unknown command: " + command.action()); } } catch (final JSONException e) { Logger.error("Could not decode action/params of command"); return new AndroidCommandResult(WDStatus.JSON_DECODER_ERROR, "Could not decode action/params of command, please check format!"); } } |
它首先叫上面的AndroidCommand解析器把json字串的action給解析出來
然后通過剛提到的map把這個action對應的CommandHandler的實現類給實例化
然后調用這個命令處理類的execute方法開始執行命令
3. 命令處理示例
我們這里就示例性的看下getText這個action對應的CommandHandler是怎么去通過AndroidElement控件進行設置文本的處理的:
public class GetText extends CommandHandler { /* * @param command The {@link AndroidCommand} used for this handler. * * @return {@link AndroidCommandResult} * * @throws JSONException * * @see io.appium.android.bootstrap.CommandHandler#execute(io.appium.android. * bootstrap.AndroidCommand) */ @Override public AndroidCommandResult execute(final AndroidCommand command) throws JSONException { if (command.isElementCommand()) { // Only makes sense on an element try { final AndroidElement el = command.getElement(); return getSuccessResult(el.getText()); } catch (final UiObjectNotFoundException e) { return new AndroidCommandResult(WDStatus.NO_SUCH_ELEMENT, e.getMessage()); } catch (final Exception e) { // handle NullPointerException return getErrorResult("Unknown error"); } } else { return getErrorResult("Unable to get text without an element."); } } } |
關鍵代碼就是里面通過AndroidCommand的getElement方法:
解析傳進來的AndroidCommand實例保存的pc端過來的json字串,找到’params‘項的子項’elementId'
通過這個獲得的id去控件哈希表(請查看《Appium Android Bootstrap源碼分析之控件AndroidElement》)中找到目標AndroidElement控件對象
然后調用獲得的AndroidElement控件對象的getText方法:
最終通過調用AndroidElement控件成員UiObject控件對象的getText方法取得控件文本信息
4. 小結
bootstrap接收到appium從pc端發送過來的json格式的鍵值對字串有多個項:
cmd: 這是一個action還是一個shutdown
action:如果是一個action的話,那么是什么action,比如click
params:擁有其他的一些子項,比如指定操作控件在AndroidElementHash維護的控件哈希表的控件鍵值的'elementId'
在收到這個json格式命令字串后:
AndroidCommandExecutor會調用AndroidCommand去解析出對應的action
然后把action去map到對應的真實命令處理方法CommandHandler的實現子類對象中
然后調用對應的對象的execute方法來執行命令
相關文章:
Appium Android Bootstrap源碼分析之簡介
Appium Android Bootstrap之控件AndroidElement
posted on 2014-12-23 00:25 順其自然EVO 閱讀(2963) 評論(0) 編輯 收藏 所屬分類: 測試學習專欄 、android