qileilove

          blog已經轉移至github,大家請訪問 http://qaseven.github.io/

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

          通過上一篇文章Appium Android Bootstrap源碼分析之控件AndroidElement》我們知道了Appium從pc端發送過來的命令如果是控件相關的話,最終目標控件在bootstrap中是以AndroidElement對象的方式呈現出來的,并且該控件對象會在AndroidElementHash維護的控件哈希表中保存起來。但是appium觸發一個命令除了需要提供是否與控件相關這個信息外,還需要其他的一些信息,比如,這個是什么命令?這個就是我們這篇文章需要討論的話題了。
            下面我們還是先看一下從pc端發過來的json的格式是怎么樣的:
            可以看到里面除了params指定的是哪一個控件之外,還指定了另外兩個信息:
            cmd: 這是一個action還是一個shutdown
            action:如果是一個action的話,那么是什么action
            開始前我們先簡要描述下我們需要涉及到幾個關鍵類:
          1. Appium命令解析器AndroidCommand
            AndroidCommand這個類真實的作用其實就是去把Appium從pc端發送過來的那串json命令解析出來,它擁有兩個成員變量:
            JSONObject         json;
            AndroidCommandType cmdType;
            json就是pc過來的json格式的那串命令,cmdType就是action或者shutdown,其實就是用來把這個類偽裝成更像個命令類而已,我認為如果不提供這個成員變量而直接修改其getType的實現去解析json字串直接獲得對應的AndroidCommandType,然后把這個類的名字改成AndroidCommandParser得了。
            那么我們往下看下AndroidCommand究竟是怎么對客戶端命令進行解析的,它的方法都很短,所以我把它做成一個表,這樣比較清晰點:
            從表中的這些方法可以看出來,這個類所做的事情基本上都是怎么去解析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

          <2014年12月>
          30123456
          78910111213
          14151617181920
          21222324252627
          28293031123
          45678910

          導航

          統計

          常用鏈接

          留言簿(55)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 高陵县| 宽城| 岳阳县| 澎湖县| 钟山县| 叙永县| 苏尼特右旗| 龙州县| 顺平县| 高要市| 革吉县| 宝坻区| 永康市| 益阳市| 丰顺县| 宁德市| 汉寿县| 新乡县| 德清县| 安图县| 平定县| 大竹县| 新郑市| 锡林郭勒盟| 达拉特旗| 白银市| 腾冲县| 嘉荫县| 南郑县| 上栗县| 镇坪县| 葫芦岛市| 施秉县| 阳曲县| 龙川县| 巴南区| 普兰县| 长子县| 准格尔旗| 普兰店市| 闸北区|