溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

UiAutomator源碼分析之注入事件

發(fā)布時(shí)間:2020-07-05 12:10:21 來源:網(wǎng)絡(luò) 閱讀:272 作者:zhukev 欄目:開發(fā)技術(shù)

上一篇文章《UiAutomator源碼分析之UiAutomatorBridge框架》中我們把UiAutomatorBridge以及它相關(guān)的類進(jìn)行的描述,往下我們會嘗試根據(jù)兩個(gè)實(shí)例將這些類給串聯(lián)起來,我準(zhǔn)備做的是用如下兩個(gè)很有代表性的實(shí)例:

  • 注入事件
  • 獲取控件
這一篇文章我們會通過分析UiDevice的pressHome這個(gè)方法來分析UiAutomator是如何注入事件的,下一篇文章會描述如何獲取控件,敬請期待。

1. UiObject.pressHome順序圖

首先我們看一下我手畫的非規(guī)范的順序圖,從中我們可以看到pressHome這個(gè)動作究竟需要和多少個(gè)類進(jìn)行交互,以及它們是怎么交互的。

UiAutomator源碼分析之注入事件

2.這些類是什么時(shí)候初始化的

在我們編寫測試用例腳本的時(shí)候我們不會對以上所有的類進(jìn)行初始化,包括UiObject對象都是通過直接在腳本中調(diào)用父類UiAutomationTestCase的getUiDevice()這個(gè)方法來獲得的。其實(shí)這些都是在uiautomator運(yùn)行時(shí)由RunTestCommand類的start()這個(gè)方法進(jìn)行初始化的,具體請看《UIAutomator源碼分析之啟動和運(yùn)行》的 3.6章節(jié)“初始化UiDevice和UiAutomationBridge“,這里就不做累述。我們這里會看下在初始化UiAutomatorBridge的時(shí)候是如何把QuneryControoler和InteractionController一并初始化了的,具體請看UiAutomatorBridge的構(gòu)造函數(shù):
/*     */   UiAutomatorBridge(UiAutomation uiAutomation) /*     */   { /*  48 */     this.mUiAutomation = uiAutomation; /*  49 */     this.mInteractionController = new InteractionController(this); /*  50 */     this.mQueryController = new QueryController(this); /*     */   }

3. 代碼跟蹤

首先看UiDevice的pressHome方法:
public boolean pressHome() { 218        Tracer.trace(); 219        waitForIdle(); 220        return getAutomatorBridge().getInteractionController().sendKeyAndWaitForEvent( 221                KeyEvent.KEYCODE_HOME, 0, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED, 222                KEY_PRESS_EVENT_TIMEOUT); 223    }
220行:
  • 獲得UiDevice對象保存的UiAutomatorBridge對象。著兩個(gè)對象都是在運(yùn)行時(shí)初始化的,不清楚的話請翻看上面提到的文章
  • 通過UiAutomatorBridge對象獲得上面章節(jié)初始化的InteractionController對象
  • 調(diào)用InteractionController對象的sendKeyAndWaitForEvent方法,里面參數(shù)關(guān)鍵是第一個(gè)keycode和第二個(gè)eventType
    • keycode:代表我們要注入的是按下哪個(gè)按鍵的事件,比如這里我們是KEYCODE_HOME
    • eventType:代表我們注射了該事件后預(yù)期會獲得窗口返回來的哪種AccessibilityEvent類型,比如我們這里是TYPE_WINDOW_CONTENT_CHANGE
進(jìn)入InteractionController類的sendKeyAndWaitForEvent:
/*     */   public boolean sendKeyAndWaitForEvent(final int keyCode, final int metaState, int eventType, long timeout) /*     */   { /* 188 */     Runnable command = new Runnable() /*     */     { /*     */       public void run() { /* 191 */         long eventTime = SystemClock.uptimeMillis(); /* 192 */         KeyEvent downEvent = new KeyEvent(eventTime, eventTime, 0, keyCode, 0, metaState, -1, 0, 0, 257); /*     */          /*     */  /* 195 */         if (InteractionController.this.injectEventSync(downEvent)) { /* 196 */           KeyEvent upEvent = new KeyEvent(eventTime, eventTime, 1, keyCode, 0, metaState, -1, 0, 0, 257); /*     */            /*     */  /* 199 */           InteractionController.this.injectEventSync(upEvent); /*     */         } /*     */          /*     */       } /* 203 */     }; /* 204 */     return runAndWaitForEvents(command, new WaitForAnyEventPredicate(eventType), timeout) != null; /*     */   }
代碼中創(chuàng)建了一個(gè)Runnable的線程,線程里面run重寫方法要做的事情就是去做注入事件的事情,那么為什么我們不直接去調(diào)用事件而需要?jiǎng)?chuàng)建一個(gè)線程了,這是因?yàn)槲覀冊谧⑷胪晔录筮€要去等待我們上面定義的預(yù)期的eventType是否有出現(xiàn)來判斷我們的事件注入究竟是否成功,這個(gè)就是204行runAndWaitForEvents做的事情。但我們這里還是先看下線程中是如何注入事件的:
/*     */   private boolean injectEventSync(InputEvent event) { /* 655 */     return this.mUiAutomatorBridge.injectInputEvent(event, true); /*     */   }
再跟蹤到UiAutomatorBridge對象:
/*     */   public boolean injectInputEvent(InputEvent event, boolean sync) { /*  70 */     return this.mUiAutomation.injectInputEvent(event, sync); /*     */   }
可以看到最終還是通過UiAutomation來注入事件的,和我們的預(yù)期是一致的。
我們繼續(xù)看InteractionController中真正執(zhí)行注入事件線程的runAndWaitForEvents方法:
/*     */   private AccessibilityEvent runAndWaitForEvents(Runnable command, UiAutomation.AccessibilityEventFilter filter, long timeout) /*     */   { /*     */     try /*     */     { /* 161 */       return this.mUiAutomatorBridge.executeCommandAndWaitForAccessibilityEvent(command, filter, timeout); /*     */     } /*     */     catch (TimeoutException e) { /* 164 */       Log.w(LOG_TAG, "runAndwaitForEvent timedout waiting for events"); /* 165 */       return null; /*     */     } catch (Exception e) { /* 167 */       Log.e(LOG_TAG, "exception from executeCommandAndWaitForAccessibilityEvent", e); } /* 168 */     return null; /*     */   }
代碼又跳到了UiAutomatorBridge這個(gè)類
/*     */   public AccessibilityEvent executeCommandAndWaitForAccessibilityEvent(Runnable command, UiAutomation.AccessibilityEventFilter filter, long timeoutMillis) throws TimeoutException /*     */   { /* 104 */     return this.mUiAutomation.executeAndWaitForEvent(command, filter, timeoutMillis); /*     */   }
最終把要執(zhí)行的runnable執(zhí)行注入事件的線程command和我們預(yù)期事件發(fā)生后返回來的窗口事件filter以及超時(shí)timeoutMillis傳進(jìn)去,UiAutomation就會和AccessibilityService進(jìn)行交互以注入事件并且等待預(yù)期AccessibilityEvent發(fā)生或者超時(shí)返回。至于UiAutomation是如何和AccessibilityService交互的,這就超出了這個(gè)系列文章的范疇了。也許今后有充裕的時(shí)間的話我們再來深入去了解分析它。


<address id="kywwc"><strike id="kywwc"></strike></address>
  • <address id="kywwc"><strike id="kywwc"></strike></address>

     

    作者

    自主博客

    微信

    CSDN

    天地會珠海分舵

    http://techgogogo.com


    服務(wù)號:TechGoGoGo

    掃描碼:

    UiAutomator源碼分析之注入事件

    向AI問一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI