appium源码分析(六)-find(下)

其实上一篇讲对find的源码分析讲的不太好,因为讲的时候,没让大家明白关于AndroidElement,以及AndroidElementsHash的定义。以及例如我们通过driver.find_element_by_name('hello').send)_keys('haha')的时候appium是根据什么来对这个元素进行操作的呢,是根据前面我们传入的hello吗?

所以这次在讲find的源码剩余内容时,我们先穿插着讲下以上的内容

AndroidElement与UiObject的关系

我们来看看AndroidElement的成员变量就一目了然了

private final UiObject el;
private String         id;
AndroidElement(final String id, final UiObject el) {
  this.el = el;
  this.id = id;
}

AndroidElement中定义了一个Uiobject的元素对象 以及一个String类型的id值,这里的UiObject元素对象,就是我们要操作的元素了。实际上我们进行点击操作时,可以看看AndroidElement的click方法

public boolean click() throws UiObjectNotFoundException {
   return el.click();
 }

实际上它直接就是调用了UiAutomator中的click方法。
再来说下String id这个值又是什么呢?或者我们先看看appium 的log是否有什么收获
这里写图片描述

由这个log大概就能够的出来,elementId 就是我们之前说的String id了。那个elementId这个值又是从哪里来的呢。

AndroidElementsHash与ElementId

我们先来看看AndroidElementsHash吧,AndroidElementsHash拥有两个成员变量

 private final Hashtable<String, AndroidElement> elements;
 private       Integer                           counter;

elements:是有个哈希表,它的key实际上就是AndroidElement的Id值,value就是AndroidElement
counter:实际上代表的是当前一共用到的控件的个数,以及后续新增一个控件的话,它就会自增1.

public static AndroidElementsHash getInstance() {
    if (AndroidElementsHash.instance == null) {
      AndroidElementsHash.instance = new AndroidElementsHash();
    }
    return AndroidElementsHash.instance;
  }
private static AndroidElementsHash instance;
/**
   * Constructor
   */
public AndroidElementsHash() {
  counter = 0;
  elements = new Hashtable<String, AndroidElement>();
}

以上的代码要说下的是AndroidElementHash是一个单例模式,这样子也保证了elements 不会再重新被初始化。构造函数则是初始化counter的值为0

我们看下addElement方法就大概能知道,AndroidElementsHash是怎么生成的了。

 public AndroidElement addElement(final UiObject element) {
    counter++;
    final String key = counter.toString();
    final AndroidElement el = new AndroidElement(key, element);
    elements.put(key, el);
    return el;
  }

这段代码应该不难理解了,每当一个新的元素增加counter的值就加1

最后我们在来分析下getElement,实际上getElement对应的就是我们平时用的find_element的方法,
但是查找实际上分为两种

  1. 基于Appium driver进行的查找
  2. 基于父控件的查找

    有了前面这些的了解 我们再来看看getElement的代码吧

 public AndroidElement getElement(final UiSelector sel, final String key)
      throws ElementNotFoundException {
    AndroidElement baseEl;
    baseEl = elements.get(key);
    UiObject el;

    if (baseEl == null) {
      el = new UiObject(sel);
    } else {
      try {
        el = baseEl.getChild(sel);
      } catch (final UiObjectNotFoundException e) {
        throw new ElementNotFoundException();
      }
    }

    if (el.exists()) {
      return addElement(el);
    } else {
      throw new ElementNotFoundException();
    }
  }

从上面的代码就可以看出来,如果说baseEl为空的情况下,这种情况就是直接通过driver进行查找
当不为空的时候,就是基于父控件的查找了。以上的查找是查找当个元素的。但是各位别忘了我们有时候还会使用到driver.find_elements()这种查看多个元素的方法。

那查找多个元素的时候与查找单个元素是一样的吗,我们来看看代码吧。

public ArrayList<AndroidElement> getElements(final UiSelector sel,
                                               final String key) throws UiObjectNotFoundException {
    boolean keepSearching = true;
    final String selectorString = sel.toString();
    final boolean useIndex = selectorString.contains("CLASS_REGEX=");
    final boolean endsWithInstance = endsWithInstancePattern.matcher(selectorString).matches();
    Logger.debug("getElements selector:" + selectorString);
    final ArrayList<AndroidElement> elements = new ArrayList<AndroidElement>();

    // If sel is UiSelector[CLASS=android.widget.Button, INSTANCE=0]
    // then invoking instance with a non-0 argument will corrupt the selector.
    //
    // sel.instance(1) will transform the selector into:
    // UiSelector[CLASS=android.widget.Button, INSTANCE=1]
    //
    // The selector now points to an entirely different element.
    if (endsWithInstance) {
      Logger.debug("Selector ends with instance.");
      // There's exactly one element when using instance.
      UiObject instanceObj = new UiObject(sel);
      if (instanceObj != null && instanceObj.exists()) {
        elements.add(addElement(instanceObj));
      }
      return elements;
    }

    UiObject lastFoundObj;
    final AndroidElement baseEl = this.getElement(key);

    UiSelector tmp;
    int counter = 0;
    while (keepSearching) {
      if (baseEl == null) {
        Logger.debug("Element[" + key + "] is null: (" + counter + ")");

        if (useIndex) {
          Logger.debug("  using index...");
          tmp = sel.index(counter);
        } else {
          tmp = sel.instance(counter);
        }
        Logger.debug("getElements tmp selector:" + tmp.toString());
        lastFoundObj = new UiObject(tmp);
      } else {
        Logger.debug("Element[" + key + "] is " + baseEl.getId() + ", counter: "
            + counter);
        lastFoundObj = baseEl.getChild(sel.instance(counter));
      }
      counter++;
      if (lastFoundObj != null && lastFoundObj.exists()) {
        elements.add(addElement(lastFoundObj));
      } else {
        keepSearching = false;
      }
    }
    return elements;
  }

查找多个元素确实会复杂很多

  • 首先getElements会判断传参来的UiSelector是否是以Instance结尾的,如果包含的话,那就直接new UiObject进行查找就可以了。
  • 如果并不是以Instance结尾的时候,还是分两种情况,首先是基于appium driver进行查找还是基于父控件的查找,再来就是直接selector查找时,后面跟上instance(counter) ,每当能够查找到元素时,counter就会自增1接着继续查找。直到没要找到对应的元素后,才会跳出循环。

    例:

self.driver.find_elements_by_class_name("android.widget.TextView")

运行的log记录即为
这里写图片描述

分析

回到最初find的源代码处

found = foundElements.size() > 0;
result = elementsToJSONArray(foundElements);

当找到的元素大于0时,将list类型的元素转化成json数组返回给server,可以从上图的log中看出。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值