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

扫描码:

tp=webp" style="max-width:100%; margin:0px; padding:0px; height:auto!important; word-wrap:break-word!important; width:auto!important; visibility:visible!important">


http://blog.csdn.net/zhubaitian

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

时间: 07-13

UiAutomator喷射事件的源代码分析的相关文章

Robotium原则的实施源代码分析

从前面的章节<Robotium源代码分析之Instrumentation进阶>中我们了解到了Robotium所基于的Instrumentation的一些进阶基础.比方它注入事件的原理等,但Robotium作为一个測试框架.其功能远不止于仅仅是方便我们注入事件,其应该还包括其它高级的功能,參照我们前面其它框架如MonkeyRunner,UiAutomator和Appium的源代码分析,我们知道一个移动平台自己主动化測试框架的基本功能除了事件注入外起码还应该有控件获取的功能. 所以,这篇文章我们主

Monkey源代码分析番外篇WindowManager如何出的喷射事件的进程间的安全限制

在分析monkey源代码时的一些背景知识不明确,例如看到monkey它是用windowmanager的injectKeyEvent的喷射事件时的方法.我发现自己陷入疙瘩,这种方法不仅能够在当前的应用程序,注入的事件它?Google在国外找到下一个大牛离开的问题的叙述性说明痕迹,特意摘录下来并做对应部分的翻译,其它部分大家喜欢就看下.我就不翻译了. How it works Behind the scenes, Monkey uses several private interfaces to c

Monkey源代码分析之事件注入

本系列的上一篇文章<Monkey源代码分析之事件源>中我们描写叙述了monkey是怎么从事件源取得命令.然后将命令转换成事件放到事件队列里面的.可是到如今位置我们还没有了解monkey里面的事件是怎么一回事,本篇文章就以这个问题作为切入点.尝试去搞清楚monkey的event架构是怎么样的.然后为什么是这样架构的,以及它又是怎么注入事件来触发点击等动作的. 在看这篇文章之前,希望大家最好先去看下另外几篇博文,这样理解起来就会更easy更清晰了: <Monkey源代码分析番外篇之Andro

转:RTMPDump源代码分析

0: 主要函数调用分析 rtmpdump 是一个用来处理 RTMP 流媒体的开源工具包,支持 rtmp://, rtmpt://, rtmpe://, rtmpte://, and rtmps://.也提供 Android 版本. 最近研究了一下它内部函数调用的关系. 下面列出几个主要的函数的调用关系. RTMPDump用于下载RTMP流媒体的函数Download: 用于建立网络连接(NetConnect)的函数Connect: 用于建立网络流(NetStream)的函数 rtmpdump源代码

Kafka SocketServer源代码分析

Kafka SocketServer源代码分析 标签: kafka 本文将详细分析Kafka SocketServer的相关源码. 总体设计 Kafka SocketServer是基于Java NIO来开发的,采用了Reactor的模式,其中包含了1个Acceptor负责接受客户端请求,N个Processor负责读写数据,M个Handler来处理业务逻辑.在Acceptor和Processor,Processor和Handler之间都有队列来缓冲请求. kafka.network.Accepto

Android万能适配器base-adapter-helper的源代码分析

项目地址:https://github.com/JoanZapata/base-adapter-helper 1. 功能介绍 1.1. base-adapter-helper base-adapter-helper 是对传统的 BaseAdapter ViewHolder 模式的一个封装.主要功能就是简化我们书写 AbsListView 的 Adapter 的代码,如 ListView,GridView. 1.2 基本使用 mListView.setAdapter(mAdapter = new

Cocos2d-X3.0 刨根问底(九)----- 场景切换(TransitionScene)源代码分析

上一章我们分析了Scene与Layer相关类的源代码,对Cocos2d-x的场景有了初步了解,这章我们来分析一下场景变换TransitionScene源代码. 直接看TransitionScene的定义 class CC_DLL TransitionScene : public Scene { public: /** Orientation Type used by some transitions */ enum class Orientation { /// An horizontal or

竖屏小游戏--喵星战争源代码分析【完整】

 转载请注明出处:http://blog.csdn.net/oyangyufu/article/details/26942311 源代码地址:http://download.csdn.net/detail/oyangyufu/7409593 Ccp文件介绍: GameMenuScene.cpp游戏主菜单 GameMainScene.cpp游戏主页面 GameObjHero.cpp主角 GameHeroBullet.cpp主角的子弹 GameObjEnemy.cpp敌人 GameEnemyBull

Raid1源代码分析--读流程

我阅读的代码的linux内核版本是2.6.32.61.刚进实验室什么都不懂,处于摸索阶段,近期的任务就是阅读raid1的源码.第一次接触raid相关的东西,网上分析源码的资料又比较少,不详细.逐行阅读代码,做了笔记.如果要对raid1的读流程有个整体上的把握,需要将笔记中的主线提炼出来,这里不写了.理解不足或者有误之处,希望批评指正. 读流程主要涉及以下函数: 请求函数make_request 读均衡read_balance 回调函数raid1_end_read_request 读出错处理rai