zoukankan      html  css  js  c++  java
  • UiAutomator喷射事件的源代码分析

    上一篇文章《UiAutomator源代码分析之UiAutomatorBridge框架》中我们把UiAutomatorBridge以及它相关的类进行的描写叙述,往下我们会尝试依据两个实例将这些类给串联起来,我准备做的是用例如以下两个非常有代表性的实例:

    • 注入事件
    • 获取控件
    这一篇文章我们会通过分析UiDevice的pressHome这种方法来分析UiAutomator是怎样注入事件的,下一篇文章会描写叙述怎样获取控件,敬请期待。


    1. UiObject.pressHome顺序图

    首先我们看一下我手画的非规范的顺序图,从中我们能够看到pressHome这个动作到底须要和多少个类进行交互,以及它们是怎么交互的。


    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

    扫描码:

    http://blog.csdn.net/zhubaitian



    版权声明:本文博客原创文章。博客,未经同意,不得转载。

  • 相关阅读:
    poj 3111 卖珠宝问题 最大化平均值
    如何在b站上传高码率视频
    AC自动机总结
    NOIP2018 旅行
    NOI2008 假面舞会 DFS
    鸽子的记录
    Unity学习笔记
    LuoguP1196 [NOI2002]银河英雄传说 并查集
    Luogu1801 黑匣子 堆
    Luogu P1321 单词覆盖还原 字符串
  • 原文地址:https://www.cnblogs.com/yxwkf/p/4646623.html
Copyright © 2011-2022 走看看