您好,登录后才能下订单哦!
通过上一篇文章《Appium Android Bootstrap源码分析之控件AndroidElement》我们知道了Appium从pc端发送过来的命令如果是控件相关的话,最终目标控件在bootstrap中是以AndroidElement对象的方式呈现出来的,并且该控件对象会在AndroidElementHash维护的控件哈希表中保存起来。但是appium触发一个命令除了需要提供是否与控件相关这个信息外,还需要其他的一些信息,比如,这个是什么命令?这个就是我们这篇文章需要讨论的话题了。
下面我们还是先看一下从pc端发过来的json的格式是怎么样的:
  
可以看到里面除了params指定的是哪一个控件之外,还指定了另外两个信息:
开始前我们先简要描述下我们需要涉及到几个关键类:
| Class | Key Method | Key Member | Parent | Description | Comment | 
| AndroidComma ndType |    | enum AndroidCommandType { ACTION,SHUTDOWN } |    | 安卓命令的类型,只有两种,shutdown的处理方式和普通的action会不一样 |    | 
| AndroidComma nd | action/getElement | JSONObject json; AndroidCommandType cmdType; |    | 从用户发过来的json命令信息得到真正的命令 |    | 
| CommandHand ler | execute |    |    | 虚拟类,其他真实CommandHandler如click的父类 |    | 
| AndroidComma ndExecutor | execute | HashMap< String, CommandHan dler> map    |    | map是所有的命令字串和真实的CommandHandler的一个映射。 其成员函数execute就是通过字串命令找到map对应的handler然后执行的 |    | 
| getText | execute |    | CommandHandler | 处理获取指定控件文本信息的类。 真正执行的是传进来的AndroidCommand对应UiObject的getText方法 | 其他click,find,drag,setText等命令同理 | 
  
JSONObject json; AndroidCommandType cmdType;json就是pc过来的json格式的那串命令,cmdType就是action或者shutdown,其实就是用来把这个类伪装成更像个命令类而已,我认为如果不提供这个成员变量而直接修改其getType的实现去解析json字串直接获得对应的AndroidCommandType,然后把这个类的名字改成AndroidCommandParser得了。
那么我们往下看下AndroidCommand究竟是怎么对客户端命令进行解析的,它的方法都很短,所以我把它做成一个表,这样比较清晰点:
| Method | Return | Code | Description | 
| AndroidCommand | N/A | 
   public AndroidCommand(final String jsonStr)  		  throws JSONException, 		  CommandTypeException {     json = new JSONObject(jsonStr);     setType(json.getString("cmd"));   } | 构造函数构造函数,把客户端过 来的json格式命 令保存起来并根 据命令的cmd项 设置好cmdType | 
| action() | String | 
   public String action()  		  throws JSONException {     if (isElementCommand()) {       return json.getString("action").     		  substring(8);     }     return json.getString("action");   } | 解析出客户端过 来的json字串的 action这个项并 返回 | 
| commandType() | AndroidCom mandType | 
   public AndroidCommandType commandType() {     return cmdType;   } | 是ACTION还是SHUTDOWN | 
| getDestElement | AndroidElement | 
   public AndroidElement getDestElement()  		  throws JSONException {     String destElId = (String) params().     		get("destElId");     return AndroidElementsHash.     		getInstance().     		getElement(destElId);   } | 解析出json字串 中params项的子 项destElId,然后 从控件哈希表中 找到目标 AndroidElement 控件返回 | 
| getElement | AndroidElement | 
   public AndroidElement getElement()  		  throws JSONException {     String elId = (String) params().     		get("elementId");     return AndroidElementsHash.getInstance().     		getElement(elId);   } | 解析出json字串 中params项的子 项elementId,然 后从控件哈希表 中找到目标 AndroidElement 控件返回 | 
| isElementCommand | boolean | 
   public boolean isElementCommand() {     if (cmdType == AndroidCommandType.ACTION) {       try {         return json.getString("action").         		startsWith("element:");       } catch (final JSONException e) {         return false;       }     }     return false;   } | 解析json字串中 的’action’项的值,如果是以’element:’ 字串开始的话就证 明是个控件相关的 命令,否则就不是 
 | 
| params | Hashtable <String, Object> | 
   public Hashtable<String, Object> params()  		  throws JSONException {     final JSONObject paramsObj =      		json.getJSONObject("params");     final Hashtable<String, Object> newParams =     		new Hashtable<String, Object>();     final Iterator<?> keys = paramsObj.keys();      while (keys.hasNext()) {       final String param = (String) keys.next();       newParams.put(param, paramsObj.get(param));     }     return newParams;   } | json字串中的params项解析器 | 
| setType | void | 
   public void setType(final String stringType)  		  throws CommandTypeException {     if (stringType.equals("shutdown")) {       cmdType = AndroidCommandType.SHUTDOWN;     } else if (stringType.equals("action")) {       cmdType = AndroidCommandType.ACTION;     } else {       throw new CommandTypeException(     		  "Got bad command type: "     				  	+ stringType);     }   } | 就是构造函数根 据json字串的 ’cmd’这个项的值 来调用这个方法 来设置的AndroidCommand Type | 
从表中的这些方法可以看出来,这个类所做的事情基本上都是怎么去解析appium从pc端过来的那串json字串。
  
  
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!要做的事情就大概就这几类:  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!");     }   }  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 getSucce***esult(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方法:
  
| 作者 | 自主博客 | 微信 | CSDN | 
| 天地会珠海分舵 | http://techgogogo.com    | 服务号:TechGoGoGo 扫描码:    | 
 免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。 相关阅读 |