Monkey源代码分析之事件注入
本系列的上一篇文章《Monkey源代码分析之事件源》中我们描写叙述了monkey是怎么从事件源取得命令。然后将命令转换成事件放到事件队列里面的。可是到如今位置我们还没有了解monkey里面的事件是怎么一回事,本篇文章就以这个问题作为切入点。尝试去搞清楚monkey的event架构是怎么样的。然后为什么是这样架构的,以及它又是怎么注入事件来触发点击等动作的。
在看这篇文章之前,希望大家最好先去看下另外几篇博文,这样理解起来就会更easy更清晰了:
- 《Monkey源代码分析番外篇之Android注入事件的三种方法比較》
- 《Monkey源代码分析番外篇之WindowManager注入事件怎样跳出进程间安全限制》
- 《Android下WindowManager的作用》
1. 事件架构
- MonkeyActivityEvent: 代表Activity相关的事件
- MonkeyMotionEvent:代表Motion相关的事件
- MonkeyKeyEvent: 代表Key相关的事件
- MonkeyFlibEvent: 代表Flib相关的事件
- MonkeyThrottleEvent:代表睡眠事件
2. 构建MonkeyKeyEvent
public class MonkeyKeyEvent extends MonkeyEvent {
private long mDownTime = -1;
private int mMetaState = -1;
private int mAction = -1;
private int mKeyCode = -1;
private int mScancode = -1;
private int mRepeatCount = -1;
private int mDeviceId = -1;
private long mEventTime = -1; private KeyEvent keyEvent = null; public MonkeyKeyEvent(int action, int keycode) {
super(EVENT_TYPE_KEY);
mAction = action;
mKeyCode = keycode;
} public MonkeyKeyEvent(KeyEvent e) {
super(EVENT_TYPE_KEY);
keyEvent = e;
} public MonkeyKeyEvent(long downTime, long eventTime, int action,
int code, int repeat, int metaState,
int device, int scancode) {
super(EVENT_TYPE_KEY); mAction = action;
mKeyCode = code;
mMetaState = metaState;
mScancode = scancode;
mRepeatCount = repeat;
mDeviceId = device;
mDownTime = downTime;
mEventTime = eventTime;
}
MonkeyKeyEvent有多个构造函数,參数都不一样,可是目的都仅仅有一个,通过传进来的參数获得足够的信息保存成成员变量。以便今后创建一个android.view.KeyEvent,皆因该系统事件就是能够依据不同的參数进行初始化的。比方以下的getEvent方法就是依据不同的參数创建相应的KeyEvent的。注意这系统KeyEvent是很重要的,由于我们今后通过WindowManager注入事件就要把它的对象传进去去驱动相应的按键相关的事件。
* @return the key event
*/
private KeyEvent getEvent() {
if (keyEvent == null) {
if (mDeviceId < 0) {
keyEvent = new KeyEvent(mAction, mKeyCode);
} else {
// for scripts
keyEvent = new KeyEvent(mDownTime, mEventTime, mAction,
mKeyCode, mRepeatCount, mMetaState, mDeviceId, mScancode);
}
}
return keyEvent;
}
支持的成员变量比較多,名字都挺浅显易懂,我这里就简单描写叙述两个我们最经常使用的:
- mAction:代表了这个keyevent的动作,就是系统KeyEvent里面定义的ACTION_DOWN,ACTION_UP或者ACTION_MULTIPLE.
- mKeyCode: 代表了你按下的到底是哪个按键,相同是在系统的KeyEvent定义的,比方82就代表了我们的系统菜单这个键值。
public static final int KEYCODE_MENU = 82;
3. 获取窗体事件注入者WindowManager
private int run(String[] args) {
...
if (!getSystemInterfaces()) {
return -3;
}
....
}
那么我们进入该方法看下我们须要的WindowManager是怎么初始化的。
private boolean getSystemInterfaces() {
mAm = ActivityManagerNative.getDefault();
if (mAm == null) {
System.err.println("** Error: Unable to connect to activity manager; is the system "
+ "running?");
return false;
} mWm = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
if (mWm == null) {
System.err.println("** Error: Unable to connect to window manager; is the system "
+ "running?");
return false;
} mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
if (mPm == null) {
System.err.println("** Error: Unable to connect to package manager; is the system "
+ "running?");
return false;
} try {
mAm.setActivityController(new ActivityController());
mNetworkMonitor.register(mAm);
} catch (RemoteException e) {
System.err.println("** Failed talking with activity manager!");
return false;
} return true;
}
这里事实上我们真正值得关注的就是WindowManager这个类,由于我们注入真实时间的时候事实上就是调用了它的方法。其它的类事实上在我们这篇文章中并没实用到的,可是既然看到了就顺便了解下吧。
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvemh1YmFpdGlhbg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">
- ActivityManager: 管理着系统的全部正在执行的activities。通过它能够获得系统正在执行的tasks,services,内存信息等。正常来说我们的应用能够同通过(ActivityManager)getSystemService(Context.ACTIVITY_SERVICE)实例化。而它提供的方法的操作都是依赖于ActivityManagerNativeProxy这个代理类来实现的
- ActivityManagerNative:ActivityManagerProxy实现了接口IActivitManager,但并不真正实现这些方法,它仅仅是一个代理类。真正动作的执行为Stub类ActivityManagerService,ActivityManagerService对象仅仅有一个并存在于system_process进程中,ActivityManagerService继承于ActivityManagerNative存根类。
- ActivityManagerProxy:代码中的第一行mAm = ActivityManagerNative.getDefault();获得的事实上就是ActivityManagerProxy的对象,而不是ActivityManagerNative
之 Window、WindowManager 与窗体管理>
- IWindowManager:WindowManager主要用来管理窗体的一些状态、属性、view添加、删除、更新、窗体顺序、消息收集和处理等,但在android1.6以后隐藏掉了。这里之所以还能调用是由于monkey是在有android源代码的情况下编译出来的。假设没有源代码的话。那么就须要用到反射机制利用Class.forName来调用获取了。
然后是PackageManager:
- PackageManager:本类API是对全部基于载入信息的数据结构的封装,包含下面功能:
- 安装,卸载应用查询permission相关信息
- 查询Application相关信息(application。activity,receiver。service,provider及对应属性等)
- 查询已安装应用
- 添加。删除permission
- 清除用户数据、缓存,代码段等
之 ServiceManager与服务管理》
- ServiceManager:ServiceMananger是android中比較重要的一个进程,它是在init进程启动之后启动。从名字上就能够看出来它是用来管理系统中的service。比方:InputMethodService、ActivityManagerService等。
在ServiceManager中有两个比較重要的方法:add_service、check_service。系统的service须要通过add_service把自己的信息注冊到ServiceManager中。当须要使用时,通过check_service检查该service是否存在
4.WindowManager往系统窗体注入事件
那么到了如今我们已经获得了要WindowManager对象了,下一步就要看MonkeyKeyEvent是怎么使用这个对象来向系统窗体发送按键key事件的了。
我们定位到injectEvent这种方法。
@Override
public int injectEvent(IWindowManager iwm, IActivityManager iam, int verbose) {
if (verbose > 1) {
String note;
if (mAction == KeyEvent.ACTION_UP) {
note = "ACTION_UP";
} else {
note = "ACTION_DOWN";
} try {
System.out.println(":Sending Key (" + note + "): "
+ mKeyCode + " // "
+ MonkeySourceRandom.getKeyName(mKeyCode));
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println(":Sending Key (" + note + "): "
+ mKeyCode + " // Unknown key event");
}
} // inject key event
try {
if (!iwm.injectKeyEvent(getEvent(), false)) {
return MonkeyEvent.INJECT_FAIL;
}
} catch (RemoteException ex) {
return MonkeyEvent.INJECT_ERROR_REMOTE_EXCEPTION;
} return MonkeyEvent.INJECT_SUCCESS;
}
注意传入參数
- iwm:这个就是我们前面获取到的WindowManager的实例对象
- iam:ActivityManager的实例对象,事实上在这里我们并不须要用到,可是为了兼容其它MonkeyXXXEvent对这个接口方法的实现,这里还是要传进来。但不作处理
5.monkey注入事件处理方式分类
@Override
public int injectEvent(IWindowManager iwm, IActivityManager iam, int verbose) { if (verbose > 1) {
System.out.println("Sleeping for " + mThrottle + " milliseconds");
}
try {
Thread.sleep(mThrottle);
} catch (InterruptedException e1) {
System.out.println("** Monkey interrupted in sleep.");
return MonkeyEvent.INJECT_FAIL;
} return MonkeyEvent.INJECT_SUCCESS;
}
所以尽管不同的MonkeyEvent实现类都实现了父类的injectEvent方法,可是并非全部的的MonkeyEvent都须要注入事件的。全部这个接口方法的名字我认为Google project师起得不好,比方叫做handleEvent就不会造成混乱了(个人见解)
事件处理方式 |
MonkeyEvent实现类 |
关键代码 |
凝视 |
通过WindowManager注入事件 |
MonkeyKeyEvent |
injectKeyiwm.injectKeyEvent(getEvent(),false)Event |
|
MonkeyTouchEvent |
iwm.injectPointerEvent(me,false) |
||
MonkeyTrackballEvent |
iwm.injectTrackballEvent(me,false) |
||
通过往事件设备/dev/input/event0发送命令注入事件 |
MonkeyFlipEvent |
FileOutputStream("/dev/input/event0") |
|
通过ActvityManager的startInstrumentation方法启动一个应用 |
MonkeyInstrumentationEvent |
iam.startInstrumentation(cn,null, |
|
睡眠 |
MonkeyThrottleEvent |
Thread.sleep(mThrottle) |
|
MonkeyWaitEvent |
Thread.sleep(mWaitTime) |
6. MonkeyEvent之Command模式
- Command :MonkeyEvent,声明了injectEvent这个execute接口方法
- ConcreteCommand: 各个MonkeyEvent实现类:MonkeyKeyEvent,MonkeyTouchEvent,MonkeyWaitEvent...
- Client : Monkey,记得它在runMonkeyCyles方法中调用了mEventSource.getNextEvent()方法来从事件源获取事件,并依据各个事件源的translateCommand方法来创建相应事件(ConcretCommand)吧?不记得的话请先看《Monkey源代码分析之执行流程》和《Monkey源代码分析之事件源》
- Receiver : WindowManager等的实例对象。由于是它们终于实施和执行了injectXXXEvent这些请求。
- Invoker : Monkey,由于直接调用MonkeyKevent(command)的injectEvent(execute)这种方法的地方依旧是在Monkey的runMonkeyCeles这种方法中:ev.injectEvent(mWm,mAm,mVerbose)。
所以Monkey在这里既扮演饿Command角色,又扮演了Invoker这个角色。
- (1)命令模式使新的命令非常easy地被添加到系统里 :诚然。假设添加个实现处理吹下屏幕的事件(Command)的话我们仅仅须要添加个类MonkeyBlowEvent,并实现injectEvent接口,然后在里面调用对应的Receiver来注入Blow这个事件即可了
- (2)同意接收请求的一方决定是否要否决请求 :这点本人没有领悟优点是什么。谁清楚的请comment
- (3)能较easy地设计一个命令队列 :确实!
monkey中就是把全部的事件抽象成MonkeyEvent然后放到我们的EventQueque里面的
- (4)能够easy地实现对请求的撤销和恢复 :这里没实用到,由于一个event消费掉后是不能撤销的。你总不能说你如今点击了个button懊悔了,程序会点击后先不运行等待你发送个undo命令吧。
只是假设用在文档编辑的undo功能中应该是挺不错的
- (5)在须要的情况下,能够较easy地将命令记入日志 :也是,每一个ConcreteCommand类都是独立的,所以想把命令记录下来是非常简单的事情该是我MonkeyKeyEvent的命令总不会变成是你MonkeyTouchEvent的命令嘛
作者 |
自主博客 |
微信 |
CSDN |
天地会珠海分舵 |
服务号:TechGoGoGo 扫描码:
tp=webp" alt="" 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 |
Monkey源代码分析之事件注入的更多相关文章
- monkey源代码分析之事件注入方法变化
在上一篇文章<Monkey源代码分析之事件注入>中.我们看到了monkey在注入事件的时候用到了<Monkey源代码分析番外篇之Android注入事件的三种方法比較>中的第一种 ...
- Monkey源代码分析番外篇之Android注入事件的三种方法比較
原文:http://www.pocketmagic.net/2012/04/injecting-events-programatically-on-android/#.VEoIoIuUcaV 往下分析 ...
- monkey源码分析之事件注入方法变化
在上一篇文章<Monkey源码分析之事件注入>中,我们看到了monkey在注入事件的时候用到了<Monkey源码分析番外篇之Android注入事件的三种方法比较>中的第一种方法 ...
- Monkey源代码分析之事件源
上一篇文章<Monkey源代码分析之执行流程>给出了monkey执行的整个流程.让我们有一个概貌,那么往后的文章我们会尝试进一步的阐述相关的一些知识点. 这里先把整个monkey类的结构图 ...
- Monkey源代码分析番外篇WindowManager如何出的喷射事件的进程间的安全限制
在分析monkey源代码时的一些背景知识不明确,例如看到monkey它是用windowmanager的injectKeyEvent的喷射事件时的方法.我发现自己陷入疙瘩,这种方法不仅能够在当前的应用程 ...
- Monkey源码分析之事件注入
本系列的上一篇文章<Monkey源码分析之事件源>中我们描述了monkey是怎么从事件源取得命令,然后将命令转换成事件放到事件队列里面的,但是到现在位置我们还没有了解monkey里面的事件 ...
- Monkey源代码分析之执行流程
在<MonkeyRunner源代码分析之与Android设备通讯方式>中.我们谈及到MonkeyRunner控制目标android设备有多种方法.当中之中的一个就是在目标机器启动一个mon ...
- 第6章7节《MonkeyRunner源代码剖析》Monkey原理分析-事件源-事件源概览-注入按键事件实例
在事件生成并放入到命令队列后,Monkey类的runMonkeyCycles就会去调用相应事件源的getNextEvent来获的事件来运行事件注入,那么这一小节我们通过MonkeyKeyEvent这个 ...
- 老李推荐:第6章7节《MonkeyRunner源码剖析》Monkey原理分析-事件源-事件源概览-注入按键事件实例
老李推荐:第6章7节<MonkeyRunner源码剖析>Monkey原理分析-事件源-事件源概览-注入按键事件实例 poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜 ...
随机推荐
- day21-python模块
1.时间 import time #时间戳 #计算 # print(time.time()) #1481321748.481654秒 #结构化时间---当地时间 # print(time.localt ...
- adb 常用命令详解
1.把电脑上文件或目录copy到手机中:adb push <local> <remote> - copy file/dir to device 此处的<local& ...
- python基础学习笔记——文件操作
文件操作 初始文件操作 使用Python来读写文件是非常简单的操作,我们使用open()函数来打开一个文件,获取到文件句柄,然后通过文件句柄就可以进行各种各样的操作了 根据打开方式的不同能够执行的操作 ...
- 00032_ArrayList集合的遍历
1.通过集合遍历,得到集合中每个元素,这是集合中最常见的操作 2.集合的遍历与数组的遍历很像,都是通过索引的方式 public class ArrayListDemo02 { public stati ...
- 18,OS模块
import os print(os.getcwd())#执行所在的目录 # os.makedirs('\python作业\景s12\day18')#可生成多层递归目录 # os.mkdir('\py ...
- 论文《Piexel Recurrent Nerual Network》总结
论文<Piexel Recurrent Nerual Network>总结 论文:<Pixel Recurrent Nerual Network> 时间:2016 作者:Aar ...
- numpy split()
numpy.split(ary, indices_or_sections, axis=0)[source] Split an array into multiple sub-arrays. 将一个ar ...
- 【JavaScript 1—基础知识点】:宏观概述
导读:JavaScript是一门新的(也可以说是旧的或者半新语言),里面有很多的知识点都能和已有的知识产生共鸣.但是,虽然简单,相同点也有很多,也有不同点.我脑袋也不好使,所以对于我来说,还是有必要再 ...
- 九度oj 题目1537:买卖股票
题目描述: 给定一个大小为n的数组,数组的元素a[i]代表第i天的股票价格. 设计一个算法,计算在最多允许买卖k次(一买一卖记为一次)的条件下的最大收益. 需要注意的是,你不能同时拥有两份股票.也就是 ...
- linux基础命令之一
1.cpio cpio(copy in/out) 功能说明:备份文件. 语 法:cpio [-0aABckLovV][-C <输入/输出大小>][-F <备份档>][-H &l ...