成人午夜视频全免费观看高清-秋霞福利视频一区二区三区-国产精品久久久久电影小说-亚洲不卡区三一区三区一区

AppiumAndroidBootstrap源碼分析之控件AndroidElement

通過上一篇文章《Appium Android Bootstrap源碼分析之簡介》我們對bootstrap的定義以及其在appium和uiautomator處于一個什么樣的位置有了一個初步的了解,那么按照正常的寫書的思路,下一個章節(jié)應(yīng)該就要去看bootstrap是如何建立socket來獲取數(shù)據(jù)然后怎樣進行處理的了。但本人覺得這樣子做并不會太好,因為到時整篇文章會變得非常的冗長,因為你在編寫的過程中碰到不認識的類又要跳入進去進行說明分析。這里我覺得應(yīng)該嘗試吸取著名的《重構(gòu)》這本書的建議:一個方法的代碼不要寫得太長,不然可讀性會很差,盡量把其分解成不同的函數(shù)。那我們這里就是用類似的思想,不要嘗試在一個文章中把所有的事情都做完,而是嘗試先把關(guān)鍵的類給描述清楚,最后才去把這些類通過一個實例分析給串起來呈現(xiàn)給讀者,這樣大家就不會因為一個文章太長影響可讀性而放棄往下學習了。

創(chuàng)新互聯(lián)-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性價比張北網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫,直接使用。一站式張北網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋張北地區(qū)。費用合理售后完善,十載實體公司更值得信賴。

那么我們這里為什么先說bootstrap對控件的處理,而非剛才提到的socket相關(guān)的socket服務(wù)器的建立呢?我是這樣子看待的,大家看到本人這篇文章的時候,很有可能之前已經(jīng)了解過本人針對uiautomator源碼分析那個系列的文章了,或者已經(jīng)有uiautomator的相關(guān)知識,所以腦袋里會比較迫切的想知道究竟appium是怎么運用了uiautomator的,那么在appium中于這個問題最貼切的就是appium在服務(wù)器端是怎么使用了uiautomator的控件的。

這里我們主要會分析兩個類:

  • AndroidElement:代表了bootstrap持有的一個ui界面的控件的類,它擁有一個UiObject成員對象和一個代表其在下面的哈希表的鍵值的String類型成員變量id
  • AndroidElementsHash:持有了一個包含所有bootstrap(也就是appium)曾經(jīng)見到過的(也就是腳本代碼中findElement方法找到過的)控件的哈希表,它的key就是AndroidElement中的id,每當appium通過findElement找到一個新控件這個id就會+1,Appium的pc端和bootstrap端都會持有這個控件的id鍵值,當需要調(diào)用一個控件的方法時就需要把代表這個控件的id鍵值傳過來讓bootstrap可以從這個哈希表找到對應(yīng)的控件

1. AndroidElement和UiObject的組合關(guān)系

從上面的描述我們可以知道,AndroidElement這個類里面擁有一個UiObject這個變量:
public class AndroidElement {    private final UiObject el;   private String         id;   ... }
大家都知道UiObject其實就是UiAutomator里面代表一個控件的類,通過它就能夠?qū)丶M行操作(當然最終還是通過UiAutomation框架). AnroidElement就是通過它來跟UiAutomator發(fā)生關(guān)系的。我們可以看到下面的AndroidElement的點擊click方法其實就是很干脆的調(diào)用了UiObject的click方法:
  public boolean click() throws UiObjectNotFoundException {     return el.click();   }
當然這里除了click還有很多控件相關(guān)的操作,比如dragTo,getText,longClick等,但無一例外,都是通過UiObject來實現(xiàn)的,這里就不一一列舉了。

2. 腳本的WebElement和Bootstrap的AndroidElement的映射關(guān)系

我們在腳本上對控件的認識就是一個WebElement:

WebElement addNote =  driver.findElementByAndroidUIAutomator("new UiSelector().text(\"Add note\")");
而在Bootstrap中一個對象就是一個AndroidElement. 那么它們是怎么映射到一起的呢?我們其實可以先看如下的代碼:

        WebElement addNote = driver.findElementByAndroidUIAutomator("new UiSelector().text(\"Add note\")");         addNote.getText();         addNote.click();
做的事情就是獲得Notes這個app的菜單,然后調(diào)用控件的getText來獲得‘Add note'控件的文本信息,以及通過控件的click方法來點擊該控件。那么我們看下調(diào)試信息是怎樣的:

Appium Android Bootstrap源碼分析之控件AndroidElement
pc端傳過來的json字串有幾個fields:

  • cmd:代表這個是什么命令類型,其實就是AndroidCommandType的那兩個值
package io.appium.android.bootstrap;  /**  * Enumeration for all the command types.  *   */ public enum AndroidCommandType {   ACTION, SHUTDOWN }
  • action: 具體命令
  • params: 提供的參數(shù),這里提供了一個elementId的鍵值對
從上面的兩條調(diào)試信息看來,其實沒有明顯的看到究竟使用的是哪個控件。其實這里不起眼的elementId就是確定用的是哪個控件的,注意這個elementId并不是一個控件在界面上的資源id,它其實是Bootstrap維護的一個保存所有已經(jīng)獲取過的控件的哈希表的鍵值。如上一小節(jié)看到的,每一個AndroidElement都有兩個重要的成員變量:

  • UiObject el:uiautomator框架中代表了一個真實的窗口控件
  • Sting id:  一個唯一的自動增加的字串類型整數(shù),pc端就是通過它來在AndroidElementHash這個類中找到想要的控件的

3. AndroidElement控件哈希表

上一節(jié)我們說到appium pc端是通過id把WebElement和目標機器端的AndroidElement映射起來的,那么我們這一節(jié)就來看下維護AndroidElement的這個哈希表是怎么實現(xiàn)的。

首先,它擁有兩個成員變量:

  private final Hashtable<String, AndroidElement> elements;   private       Integer                           counter;
  • elements:一個以AndroidElement 的id的字串類型為key,以AndroidElement的實例為value的的哈希表
  • counter: 一個整型變量,有兩個作用:其一是它代表了當前已經(jīng)用到的控件的數(shù)目(其實也不完全是,你在腳本中對同一個控件調(diào)用兩次findElement其實會產(chǎn)生兩個不同id的AndroidElement控件),其二是它代表了一個新用到的控件的id,而這個id就是上面的elements哈希表的鍵
這個哈希表的鍵值都是從0開始的,請看它的構(gòu)造函數(shù):
  /**    * Constructor    */   public AndroidElementsHash() {     counter = 0;     elements = new Hashtable<String, AndroidElement>();   }
而它在整個Bootstrap中是有且只有一個實例的,且看它的單例模式實現(xiàn):
  public static AndroidElementsHash getInstance() {     if (AndroidElementsHash.instance == null) {       AndroidElementsHash.instance = new AndroidElementsHash();     }     return AndroidElementsHash.instance;   }
以下增加一個控件的方法addElement充分描述了為什么說counter是一個自增加的key,且是每個新發(fā)現(xiàn)的AndroidElement控件的id:
  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;   }
從Appium發(fā)過來的控件查找命令大方向上分兩類:
  • 1. 直接基于Appium Driver來查找,這種情況下appium發(fā)過來的json命令是不包含控件哈希表的鍵值信息的
[java] view plaincopy
  1. WebElement addNote = driver.findElement(By.name("Add note"));  
  • 2. 基于父控件查找:
[java] view plaincopy
  1. WebElement el = driver.findElement(By.className("android.widget.ListView")).findElement(By.name("Note1"));  
以上的腳本會先嘗試找到Note1這個日記的父控件ListView,并把這個控件保存到控件哈希表,然后再根據(jù)父控件的哈希表鍵值以及子控件的選擇子找到想要的Note1:
Appium Android Bootstrap源碼分析之控件AndroidElement
AndroidElementHash的這個getElement命令要做的事情就是針對這兩點來根據(jù)不同情況獲得目標控件
[java] view plaincopy
  1. /** 
  2.  * Return an elements child given the key (context id), or uses the selector 
  3.  * to get the element. 
  4.  *  
  5.  * @param sel 
  6.  * @param key 
  7.  *          Element id. 
  8.  * @return {@link AndroidElement} 
  9.  * @throws ElementNotFoundException 
  10.  */  
  11. public AndroidElement getElement(final UiSelector sel, final String key)  
  12.     throws ElementNotFoundException {  
  13.   AndroidElement baseEl;  
  14.   baseEl = elements.get(key);  
  15.   UiObject el;  
  16.   
  17.   if (baseEl == null) {  
  18.     el = new UiObject(sel);  
  19.   } else {  
  20.     try {  
  21.       el = baseEl.getChild(sel);  
  22.     } catch (final UiObjectNotFoundException e) {  
  23.       throw new ElementNotFoundException();  
  24.     }  
  25.   }  
  26.   
  27.   if (el.exists()) {  
  28.     return addElement(el);  
  29.   } else {  
  30.     throw new ElementNotFoundException();  
  31.   }  
  32. }  
  • 如果是第1種情況就直接通過選擇子構(gòu)建UiObject對象,然后通過addElement把UiObject對象轉(zhuǎn)換成AndroidElement對象保存到控件哈希表
  • 如果是第2種情況就先根據(jù)appium傳過來的控件哈希表鍵值獲得父控件,再通過子控件的選擇子在父控件的基礎(chǔ)上查找到目標UiObject控件,最后跟上面一樣把該控件通過上面的addElement把UiObject控件轉(zhuǎn)換成AndroidElement控件對象保存到控件哈希表




4. 求證

上面有提過,如果pc端的腳本執(zhí)行對同一個控件的兩次findElement會創(chuàng)建兩個不同id的AndroidElement并存放到控件哈希表中,那么為什么appium的團隊沒有做一個增強,增加一個keyMap的方法(算法)和一些額外的信息來讓同一個控件使用不同的key的時候?qū)?yīng)的還是同一個AndroidElement控件呢?畢竟這才是哈希表實用的特性之一了,不然你直接用一個Dictionary不就完事了?網(wǎng)上說了幾點hashtable和dictionary的差別,如多線程環(huán)境最好使用哈希表而非字典等,但在bootstrap這個控件哈希表的情況下我不是很信服這些說法,有誰清楚的還勞煩指點一二了
這里至于為什么appium不去提供額外的key信息并且實現(xiàn)keyMap算法,我個人倒是認為有如下原因:
  • 有誰這么無聊在同一個測試方法中對同一個控件查找兩次?
  • 如果同一個控件運用不同的選擇子查找兩次的話,因為最終底層的UiObject的成員變量UiSelector mSelector不一樣,所以確實可以認為是不同的控件
但以下兩個如果用同樣的UiSelector選擇子來查找控件的情況我就解析不了了,畢竟在我看來bootstrap這邊應(yīng)該把它們看成是同一個對象的:
  • 同一個腳本不同的方法中分別對同一控件用同樣的UiSelelctor選擇子進行查找呢?
  • 不同腳本中呢?
這些也許在今后深入了解中得到解決,但看家如果知道的,還望不吝賜教

5. 小結(jié)

最后我們對bootstrap的控件相關(guān)知識點做一個總結(jié)
  • AndroidElement的一個實例代表了一個bootstrap的控件
  • AndroidElement控件的成員變量UiObject el代表了uiautomator框架中的一個真實窗口控件,通過它就可以直接透過uiautomator框架對控件進行實質(zhì)性操作
  • pc端的WebElement元素和Bootstrap的AndroidElement控件是通過AndroidElement控件的String id進行映射關(guān)聯(lián)的
  • AndroidElementHash類維護了一個以AndroidElement的id為鍵值,以AndroidElement的實例為value的全局唯一哈希表,pc端想要獲得一個控件的時候會先從這個哈希表查找,如果沒有了再創(chuàng)建新的AndroidElement控件并加入到該哈希表中,所以該哈希表中維護的是一個當前已經(jīng)使用過的控件

         

        作者

        自主博客

        微信

        CSDN

        天地會珠海分舵

        http://techgogogo.com


        服務(wù)號:TechGoGoGo

        掃描碼:

        Appium Android Bootstrap源碼分析之控件AndroidElement

        AppiumAndroidBootstrap源碼分析之控件AndroidElement
        轉(zhuǎn)載源于:http://jinyejixie.com/article34/ghosse.html

        成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供定制網(wǎng)站動態(tài)網(wǎng)站、云服務(wù)器外貿(mào)建站、軟件開發(fā)、網(wǎng)站設(shè)計

        廣告

        聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

        阿拉尔市| 忻州市| 泽库县| 施甸县| 亳州市| 海宁市| 盈江县| 澜沧| 延川县| 侯马市| 府谷县| 洛南县| 滨州市| 左权县| 慈利县| 康保县| 新郑市| 江孜县| 海城市| 莱芜市| 昌都县| 安新县| 渝北区| 堆龙德庆县| 沙坪坝区| 前郭尔| 兴山县| 大竹县| 恭城| 顺义区| 长宁区| 静乐县| 凉山| 玉林市| 忻城县| 三台县| 梅州市| 许昌市| 如皋市| 五峰| 邓州市|