Appium源码分析(六)-find(上)

说明:


原本打算按照项目的目录,将源码都分析一遍,但是其中遇到部分有些不太理解的,还有部分是依赖于另外一些代码的。所以这次特地先抽出相当重要的一块代码 find进行解释。后续的click,setText等等都是依赖于find才能够进行的,所以先抽出find来进行讲解

正文:


首先我们先来看看find的源代码
private AndroidCommandResult execute(final AndroidCommand command,
      final boolean isRetry) throws JSONException {
     //这里是解析出command中的每一个参数,将其转换成Hashtable,还是类似于key-value
    final Hashtable<String, Object> params = command.params();
    // only makes sense on a device
    final Strategy strategy;
    try {
       //判断参数中是否有strategy,如果没有直接返回错误异常
      strategy = Strategy.fromString((String) params.get("strategy"));
    } catch (final InvalidStrategyException e) {
      return new AndroidCommandResult(WDStatus.UNKNOWN_COMMAND, e.getMessage());
    }

    final String contextId = (String) params.get("context");
    final String text = (String) params.get("selector");
    final boolean multiple = (Boolean) params.get("multiple");

    Logger.debug("Finding " + text + " using " + strategy.toString()
        + " with the contextId: " + contextId + " multiple: " + multiple);
    boolean found = false;
    try {
      Object result = null;
      //通过getSelector 获取一个UiSelector的列表
      final List<UiSelector> selectors = getSelectors(strategy, text, multiple);
      if (!multiple) {
      //这里会遍历selector列表,并且在没找到元素对象的时候,如果找到了直接跳出循环,不会继续往下进行查找
        for (int i = 0; i < selectors.size() && !found; i++) {
          try {
            Logger.debug("Using: " + selectors.get(i).toString());
            result = fetchElement(selectors.get(i), contextId);
            found = result != null;
          } catch (final ElementNotFoundException ignored) {
          }
        }
      } else {
        List<AndroidElement> foundElements = new ArrayList<AndroidElement>();
        for (final UiSelector sel : selectors) {
          // With multiple selectors, we expect that some elements may not
          // exist.
          try {
            Logger.debug("Using: " + sel.toString());
            final List<AndroidElement> elementsFromSelector = fetchElements(
                sel, contextId);
            foundElements.addAll(elementsFromSelector);
          } catch (final UiObjectNotFoundException ignored) {
          }
        }
        if (strategy == Strategy.ANDROID_UIAUTOMATOR) {
          foundElements = ElementHelpers.dedupe(foundElements);
        }
        found = foundElements.size() > 0;
        result = elementsToJSONArray(foundElements);
      }

      if (!found) {
        if (!isRetry) {
          Logger
              .debug("Failed to locate element. Clearing Accessibility cache and retrying.");
          // some control updates fail to trigger AccessibilityEvents, resulting
          // in stale AccessibilityNodeInfo instances. In these cases, UIAutomator 
          // will fail to locate visible elements. As a work-around, force clear 
          // the AccessibilityInteractionClient's cache and search again. This 
          // technique also appears to make Appium's searches conclude more quickly.
          // See Appium issue #4200 https://github.com/appium/appium/issues/4200
          if (ReflectionUtils.clearAccessibilityCache()) {
            return execute(command, true);
          }
        }
        // JSONWP spec does not return NoSuchElement
        if (!multiple) {
          // If there are no results and we've already retried, return an error.
          return new AndroidCommandResult(WDStatus.NO_SUCH_ELEMENT,
              "No element found");
        }
      }

      return getSuccessResult(result);
    } catch (final InvalidStrategyException e) {
      return getErrorResult(e.getMessage());
    } catch (final UiSelectorSyntaxException e) {
      return new AndroidCommandResult(WDStatus.UNKNOWN_COMMAND, e.getMessage());
    } catch (final ElementNotFoundException e) {
      return new AndroidCommandResult(WDStatus.NO_SUCH_ELEMENT, e.getMessage());
    } catch (final ParserConfigurationException e) {
      return getErrorResult("Error parsing xml hierarchy dump: "
          + e.getMessage());
    } catch (final InvalidSelectorException e) {
      return new AndroidCommandResult(WDStatus.INVALID_SELECTOR, e.getMessage());
    }
  }
我们来看看getSelectors吧,它是如何通过你的strategy, text, multiple来获取到UiSelector列表的。这里的multiple代表的是你要查找的元素是一个还是多个
 private List<UiSelector> getSelectors(final Strategy strategy,
      final String text, final boolean many) throws InvalidStrategyException,
      ElementNotFoundException, UiSelectorSyntaxException,
      ParserConfigurationException, InvalidSelectorException {
    final List<UiSelector> selectors = new ArrayList<UiSelector>();
    UiSelector sel = new UiSelector();

    switch (strategy) {
      case XPATH:
        for (final UiSelector selector : getXPathSelectors(text, many)) {
          selectors.add(selector);
        }
        break;
      case CLASS_NAME:
        sel = sel.className(text);
        if (!many) {
          sel = sel.instance(0);
        }
        selectors.add(sel);
        break;
      case ID:
        // There are three types of ids on Android.
        // 1. resourceId (API >= 18)
        // 2. accessibility id (content description)
        // 3. strings.xml id
        //
        // If text is a resource id then only use the resource id selector.
        if (API_18) {
          if (resourceIdRegex.matcher(text).matches()) {
            sel = sel.resourceId(text);
            if (!many) {
              sel = sel.instance(0);
            }
            selectors.add(sel);
            break;
          } else {
            // not a fully qualified resource id
            // transform "textToBeChanged" into:
            // com.example.android.testing.espresso.BasicSample:id/textToBeChanged
            // android:id/textToBeChanged
            // either it's prefixed with the app package or the android system page.
            String pkg = (String) params.get("pkg");

            if (pkg != null) {
              sel = sel.resourceId(pkg + ":id/" + text);
              if (!many) {
                sel = sel.instance(0);
              }
              selectors.add(sel);
            }

            sel = sel.resourceId("android:id/" + text);
            if (!many) {
              sel = sel.instance(0);
            }
            selectors.add(sel);
          }
        }

        // must create a new selector or the selector from
        // the resourceId search will cause problems
        sel = new UiSelector().description(text);
        if (!many) {
          sel = sel.instance(0);
        }
        selectors.add(sel);

        // resource id and content description failed to match
        // so the strings.xml selector is used
        final UiSelector stringsXmlSelector = stringsXmlId(many, text);
        if (stringsXmlSelector != null) {
          selectors.add(stringsXmlSelector);
        }
        break;
      case ACCESSIBILITY_ID:
        sel = sel.description(text);
        if (!many) {
          sel = sel.instance(0);
        }
        selectors.add(sel);
        break;
      case NAME:
        sel = new UiSelector().description(text);
        if (!many) {
          sel = sel.instance(0);
        }
        selectors.add(sel);

        sel = new UiSelector().text(text);
        if (!many) {
          sel = sel.instance(0);
        }
        selectors.add(sel);
        break;
      case ANDROID_UIAUTOMATOR:
        List<UiSelector> parsedSelectors;
        try {
          parsedSelectors = uiAutomatorParser.parse(text);
        } catch (final UiSelectorSyntaxException e) {
          throw new UiSelectorSyntaxException(
              "Could not parse UiSelector argument: " + e.getMessage());
        }

        for (final UiSelector selector : parsedSelectors) {
          selectors.add(selector);
        }

        break;
      case LINK_TEXT:
      case PARTIAL_LINK_TEXT:
      case CSS_SELECTOR:
      default:
        throw new InvalidStrategyException("Sorry, we don't support the '"
            + strategy.getStrategyName() + "' locator strategy yet");
    }

    return selectors;
  }
代码有点长,我们先逐一的进行分析吧,
xpath:这个暂时还没明白,先过吧。。
class_name:
case CLASS_NAME:
    //这里比较简单直接是通过UiSelector的className进行查找,但是问题是如果是many为true的情况下,实际上selectors仍然只是一个而已,所以这个地方没发现为什么能够返回List的UiSelector
    sel = sel.className(text);
    if (!many) {
      sel = sel.instance(0);
    }
    selectors.add(sel);
    break;

#### id

 if (API_18) {
     if (resourceIdRegex.matcher(text).matches()) {
       sel = sel.resourceId(text);
       if (!many) {
         sel = sel.instance(0);
       }
       selectors.add(sel);
       break;
     } else {
       // not a fully qualified resource id
       // transform "textToBeChanged" into:
       // com.example.android.testing.espresso.BasicSample:id/textToBeChanged
       // android:id/textToBeChanged
       // either it's prefixed with the app package or the android system page.
       String pkg = (String) params.get("pkg");

       if (pkg != null) {
         sel = sel.resourceId(pkg + ":id/" + text);
         if (!many) {
           sel = sel.instance(0);
         }
         selectors.add(sel);
       }

       sel = sel.resourceId("android:id/" + text);
       if (!many) {
         sel = sel.instance(0);
       }
       selectors.add(sel);
     }
ID这里分了两种情况就是你传入的ID是否包含了对应的包名:
  1. 如果不包含则尝试给你的ID值加上你的测试软件的包名,或者是系统自带的包名(android:id/),这里的判断是根据参数中是否有pkg.
  2. 再统一进行UiSelector的resourceId的查找,这里的话会要求api的版本是要大于18。如何API的版本小于18 这个时候selector就采用description的方式进行查找。
  3. 如果说id的方式以及description的方式都匹配失败的情况下 最后的方案就是使用stringxml,匹配到对应的内容
AccessibilityId

实际上就是description进行查找

name

这里的name匹配说来有点坑了,实际上它说先找的还是description,如果说description没有找到的情况下,它才会去匹配name这个属性。

android_UiAutomator 关于UIAutomator的相关命令 后续会有相应的篇章来说明

就先讲到这里,下一篇,我们来看看查找多个元素的时候,appium又做了哪些处理呢。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值