首页 > 代码库 > 第6章1节《MonkeyRunner源代码剖析》Monkey原理分析-事件源-事件源概览
第6章1节《MonkeyRunner源代码剖析》Monkey原理分析-事件源-事件源概览
在上一章中我们有简要的介绍了事件源是怎么一回事。可是并没有进行详细的描写叙述。那么往下的这几个小节我们就须要把这方面的知识给补充完整。
这一节我们先主要环绕MonkeySourceNetwork这个事件源来学习事件源的框架结构。首先,要理解事件源,必须先搞清楚几个问题:
- 事件从哪里来?
Monkey的事件来源有多个方面,可是作为MonkeyRunner框架的一部分,它的事件来源主要是来自MonkeyRunner通过网络Socket(USB/TCP协议)发送过来的命令字串。MonkeySourceNetwork这个事件源类就是专门处理这些请求的。MonkeySourceNetwork会在初始化的过程中建立一个ServerSocket来供client连接。Socket的端口就是MonkeyRunner通过ADB shell发送给Android目标机器的启动monkey的命令“monkey –port 12345”中的12345。
556 public MonkeySourceNetwork(int port) throws IOException { 557 // Only bind this to local host. This means that you can only 558 // talk to the monkey locally, or though adb port forwarding. 559 serverSocket = new ServerSocket(port, 560 0, // default backlog 561 InetAddress.getLocalHost()); 562 }代码6-1-1 MonkeySourceNetwork - 构造函数
- 来自网络的事件命令字串怎样转换成事件?
来自网络的字串是不能直接使用的,Monkey必须把该命令字串进行解析,在必要的时候转换成相应的Monkey事件,这个过程在Monkey中称为命令翻译。
MonkeySourceNetwork一旦从MonkeyRunnerclient获取一个字串命令,它就会依据其内部成员COMMAND_MAP这个“字串命令 - 命令翻译类对象”的映射表,检索到该命令字串相应的翻译类对象。然后就会调用它的命令翻译方法来把该字串命令翻译成相应的MonkeyEvent事件,这里说的MonkeyEvent是全部详细事件如MonkeyFlipEvent的父类。
下面代码就是COMMAND_MAP在MonkeySourceNetwork类中的实现:
449 private static final Map<String, MonkeyCommand> COMMAND_MAP = new HashMap<String, MonkeyCommand>(); 450 451 static { 452 // Add in all the commands we support 453 COMMAND_MAP.put("flip", new FlipCommand()); 454 COMMAND_MAP.put("touch", new TouchCommand()); 455 COMMAND_MAP.put("trackball", new TrackballCommand()); 456 COMMAND_MAP.put("key", new KeyCommand()); 457 COMMAND_MAP.put("sleep", new SleepCommand()); 458 COMMAND_MAP.put("wake", new WakeCommand()); 459 COMMAND_MAP.put("tap", new TapCommand()); 460 COMMAND_MAP.put("press", new PressCommand()); 461 COMMAND_MAP.put("type", new TypeCommand()); 462 COMMAND_MAP.put("listvar", new MonkeySourceNetworkVars.ListVarCommand()); 463 COMMAND_MAP.put("getvar", new MonkeySourceNetworkVars.GetVarCommand()); 464 COMMAND_MAP.put("listviews", new MonkeySourceNetworkViews.ListViewsCommand()); 465 COMMAND_MAP.put("queryview", new MonkeySourceNetworkViews.QueryViewCommand()); 466 COMMAND_MAP.put("getrootview", new MonkeySourceNetworkViews.GetRootViewCommand()); 467 COMMAND_MAP.put("getviewswithtext", 468 new MonkeySourceNetworkViews.GetViewsWithTextCommand()); 469 COMMAND_MAP.put("deferreturn", new DeferReturnCommand()); 470 }代码6-1-2 MonkeySourceNetwork - COMMAND_MAP
它的键是String类型的字串。代表的是从网络过来的命令字串。它的值是MonkeyCommand的实例,代表的是负责将该命令字串翻译成相应事件的类实例。但要注意的是并非全部的命令都会生成相应的事件对象并放到事件队列里等待运行,有些命令会在翻译的过程中直接处理返回的。
往下描写叙述MonkeyEvent事件的时候会有更详尽的描写叙述。
COMMAND_MAP里面的键记录的仅仅是命令字串,没有包括相应的參数,下面列出Monkey支持的从网络过来的全部命令和相应的參数:
命令字串格式 | MR是否支持 | 凝视 |
touch down x y | 是 | x代表x坐标,y代表y坐标 |
touch up x y | 是 | 同上 |
touch move x y | 是 | 同上 |
tap x y | 是 | 同上 |
press name | 是 | Name代表按键名。如"MENU", "HOME", "SEARCH"等 |
key down name | 是 | 同上 |
key up name | 是 | 同上 |
getvar name | 是 | name 代表属性名 |
listvar | 是 | |
type line | 是 | line 代表输入字串 |
wake | 是 | |
listViews | 是 | |
queryview | 是 | |
getRootView | 是 | |
getViewWithText | 是 | |
done | 是 | 測试完毕,Monkey收到命令后会停止Socket监听 |
quit | 是 | 測试请求退出,Monkey收到后不会停止Socket监听, |
flip open | 否 | MonkeyRunner不支持发送这两个命令 |
flip close | 否 | |
trackball dx dy | 否 | |
deferredReturn | 否 | 这个命令比較特别,做的事情是等待一个命令完毕然后运行另外一个命令。可是在MonkeyRunner框架中并没有支持。 |
注: MR代表MonkeyRunner |
表6-1-1 命令字串參照表
- 事件要到那里去?
每一个事件源处理类都维护着一个自己的事件队列, 在Monkey中叫做CommandQueue。里面装的是每一个详细的MonkeyEvent事件。当来自网络的字串命令被翻译成相应的MonkeyEvent的时候就会把它追加到事件队列里面
了解了以上几个问题,我们脑袋里就有MonkeySourceNetwork这个事件源的主要的概念了,下面简要总结下MonkeySourceNetwork的主要作用:
- 从网络获取命令字串
- 使用相应的命令翻译类把命令字串翻译成MonkeyEvent事件并把事件放入到命令队列CommandQueue
最后放到CommandQueue里面做什么呢?当然是给Monkey类的runMonkeyCycles方法去消费事件了。还记得它每一个循环都会调用mEventSource.getNextEvent去获取一个事件然后进行事件注入吧?详细请查看上一章第7节runMonkeyCycles方法的介绍。
下图演示样例显示了从MonkeyRunner測试脚本调用MonkeyDevice的type方法来往系统窗体输入字串開始。到该type命令字串由网络事件源获取到并翻译成相应的事件,增加事件队列。最后取出事件进行事件注入的流程。当中红色方框圈住部分就是刚才描写叙述的网络事件源类MonkeySourceNetwork主要做的事情。
图6-1-1 事件处理示意图
下面我们看下事件源类的族谱,这里仅仅有我们须要重点描写叙述的MonkeySourceNetwork事件源会把重要的成员方法和成员变量显示出来,但这不代表其它事件源就仅仅有一个getNextEvent方法,仅仅是它们在MonkeyRunner框架中不起什么作用,所以省略了而已:
图6-1-2 事件源族谱
MonkeyEventSource定义了全部事件源的接口,里面定义了全部实现该接口的事件源都必须实现getNextEvent的方法来从各自相应的事件队列CommandQueue中取事件,事实上这里CommandQueue更应该叫做EventQueue,这样会更easy理解,由于它里面装的都是事件。这个设计非常好的体现了面向对象的多态特性,由于这种话在上一章描写叙述的runMonkeyCycles方法中就仅仅须要调用MonkeyEventSource接口实例mEventSource的getNextEvent方法就能取得一个事件来运行了。而它根本不须要知道调用的是哪个事件源来获得事件的,由于mEventSource指定的就是全部事件的顶层接口MonkeyEventSource。下面重点描写叙述下跟MonkeyRunner框架相关的事件源:
- MonkeySourceNetworkVars和MonkeySourceNetworkViews并不会直接实现MonkeyEventSource接口,事实上它们的事件都是通过MonkeySourceNetwork来从网络获得的,MonkeySourceNetwork在获得网络过来的事件后,假设发现它们是根获取系统属性和获取界面控件有关,那么就会分别调用MonkeySourceNetworkVars和MonkeySourceNetworkViews里面相应的MonkeyCommand内部类的方法translateCommand来把网络过来的字串事件命令翻译成相应的MonkeyEvent
- 这里MonkeySourceNetwork这个事件源是我们分析的重点。它持有成员变量commandQueue,里面装的就是全部将要运行的MonkeyEvent事件
- MonkeySourceNetwork内部建立了一个网络过来的命令字串到相应的MonkeyCommand实例的映射,MonkeyCommand是专门用来将该命令字串翻译成MonkeyEvent事件的
- MonkeySourceNetwork在初始化时会建立一个ServerSocket来接收从网络过来的也就是MonkeyRunnerclient发送过来的字串命令。并调用自身的translateCommand方法来依据该命令名字在分发到其在CommandMap相应的MonkeyCommand来把它翻译成相应的事件
最后我们看下处理网络事件所涉及到的全部关键类的类关系图,先做初步了解以有个初步概念,往下的小节我们将会进行更详细的分析:
图6-1-3 事件处理关键类关系图
依据上一章的分析。Monkey类的runMonkeyCycles方法会循环运行,每循环一次都会调用事件源mEventSource的getNextEvent方法来获得一个事件,在这里就是MonkeySourceNetwork事件源的getNextEvent方法。然后调用该事件的injectEvent方法来处理相应的事件注入请求。
那么我们结合上面的关键类关系图看每一个类之间的关系及交互是怎么样的:
- Monkey组合MonkeySourceNetwork: Monkey拥有私有成员mEventSource,该成员变量在MonkeyRunner请求”monkey --port 12345”启动monkey的过程中会初始化成MonkeySourceNetwork的实例,详情请看上一章的第6小节”初始化事件源”
- MonkeySourceNetwork组合CommandQueueImpl: MonkeySourceNetwork维护着一个commandQueue的成员变量,这个成员变量是一个保存着全部等待运行的事件的一个队列,也就是我们常常提及的MonkeyEvent事件队列
- CommandQueueImpl组合 MonkeyEvent: 事件队列里边包括有0到多个Monkey事件MonkeyEvent的实例。Monkey的runMonkeyCycles方法里面的每一个cycle都会去该命令队列中取出一条命令来运行
注:很多其它文章请关注公众号:techgogogo或个人博客http://techgogogo.com。
当然。也非常欢迎您直接微信(zhubaitian1)勾搭。本文由天地会珠海分舵原创。转载请自觉,是否投诉维权看心情。
第6章1节《MonkeyRunner源代码剖析》Monkey原理分析-事件源-事件源概览