溫馨提示×

溫馨提示×

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

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

UiAutomator源碼分析之注入事件

發布時間:2020-07-05 12:10:21 來源:網絡 閱讀:285 作者:zhukev 欄目:開發技術

上一篇文章《UiAutomator源碼分析之UiAutomatorBridge框架》中我們把UiAutomatorBridge以及它相關的類進行的描述,往下我們會嘗試根據兩個實例將這些類給串聯起來,我準備做的是用如下兩個很有代表性的實例:

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

1. UiObject.pressHome順序圖

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

UiAutomator源碼分析之注入事件

2.這些類是什么時候初始化的

在我們編寫測試用例腳本的時候我們不會對以上所有的類進行初始化,包括UiObject對象都是通過直接在腳本中調用父類UiAutomationTestCase的getUiDevice()這個方法來獲得的。其實這些都是在uiautomator運行時由RunTestCommand類的start()這個方法進行初始化的,具體請看《UIAutomator源碼分析之啟動和運行》的 3.6章節“初始化UiDevice和UiAutomationBridge“,這里就不做累述。我們這里會看下在初始化UiAutomatorBridge的時候是如何把QuneryControoler和InteractionController一并初始化了的,具體請看UiAutomatorBridge的構造函數:
/*     */   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對象。著兩個對象都是在運行時初始化的,不清楚的話請翻看上面提到的文章
  • 通過UiAutomatorBridge對象獲得上面章節初始化的InteractionController對象
  • 調用InteractionController對象的sendKeyAndWaitForEvent方法,里面參數關鍵是第一個keycode和第二個eventType
    • keycode:代表我們要注入的是按下哪個按鍵的事件,比如這里我們是KEYCODE_HOME
    • eventType:代表我們注射了該事件后預期會獲得窗口返回來的哪種AccessibilityEvent類型,比如我們這里是TYPE_WINDOW_CONTENT_CHANGE
進入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; /*     */   }
代碼中創建了一個Runnable的線程,線程里面run重寫方法要做的事情就是去做注入事件的事情,那么為什么我們不直接去調用事件而需要創建一個線程了,這是因為我們在注入完事件之后還要去等待我們上面定義的預期的eventType是否有出現來判斷我們的事件注入究竟是否成功,這個就是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來注入事件的,和我們的預期是一致的。
我們繼續看InteractionController中真正執行注入事件線程的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這個類
/*     */   public AccessibilityEvent executeCommandAndWaitForAccessibilityEvent(Runnable command, UiAutomation.AccessibilityEventFilter filter, long timeoutMillis) throws TimeoutException /*     */   { /* 104 */     return this.mUiAutomation.executeAndWaitForEvent(command, filter, timeoutMillis); /*     */   }
最終把要執行的runnable執行注入事件的線程command和我們預期事件發生后返回來的窗口事件filter以及超時timeoutMillis傳進去,UiAutomation就會和AccessibilityService進行交互以注入事件并且等待預期AccessibilityEvent發生或者超時返回。至于UiAutomation是如何和AccessibilityService交互的,這就超出了這個系列文章的范疇了。也許今后有充裕的時間的話我們再來深入去了解分析它。


 

作者

自主博客

微信

CSDN

天地會珠海分舵

http://techgogogo.com


服務號:TechGoGoGo

掃描碼:

UiAutomator源碼分析之注入事件

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女