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

来源:互联网 发布:淘宝达人就是微淘吗 编辑:程序博客网 时间:2024/05/20 16:32

其实上一篇讲对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 0
原创粉丝点击