Monkey源码分析之事件注入
来源:互联网 发布:学生防网络诈骗漫画 编辑:程序博客网 时间:2024/06/03 05:59
本系列的上一篇文章《Monkey源码分析之事件源》中我们描述了monkey是怎么从事件源取得命令,然后将命令转换成事件放到事件队列里面的,但是到现在位置我们还没有了解monkey里面的事件是怎么一回事,本篇文章就以这个问题作为切入点,尝试去搞清楚monkey的event架构是怎么样的,然后为什么是这样架构的,以及它又是怎么注入事件来触发点击等动作的。
在看这篇文章之前,希望大家最好先去看下另外几篇博文,这样理解起来就会更容易更清晰了:
- 《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;
- }
- * @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;
- }
- ....
- }
- 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;
- }
- 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
- IWindowManager:WindowManager主要用来管理窗口的一些状态、属性、view增加、删除、更新、窗口顺序、消息收集和处理等,但在android1.6以后隐藏掉了。这里之所以还能调用是因为monkey是在有android源码的情况下编译出来的,如果没有源码的话,那么就需要用到反射机制利用Class.forName来调用获取了。
然后是PackageManager:
- PackageManager:本类API是对所有基于加载信息的数据结构的封装,包括以下功能:
- 安装,卸载应用查询permission相关信息
- 查询Application相关信息(application,activity,receiver,service,provider及相应属性等)
- 查询已安装应用
- 增加,删除permission
- 清除用户数据、缓存,代码段等
- 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实现类
关键代码
注释
通过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, 0,args,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)命令模式使新的命令很容易地被加入到系统里 :诚然!如果增加个实现处理吹下屏幕的事件(Command)的话我们只需要增加个类MonkeyBlowEvent,并实现injectEvent接口,然后在里面调用相应的Receiver来注入Blow这个事件就行了
- (2)允许接收请求的一方决定是否要否决请求 :这点本人没有领悟好处是什么,谁清楚的请comment
- (3)能较容易地设计一个命令队列 :确实!monkey中就是把所有的事件抽象成MonkeyEvent然后放到我们的EventQueque里面的
- (4)可以容易地实现对请求的撤销和恢复 :这里没有用到,因为一个event消费掉后是不能撤销的。你总不能说你现在点击了个按钮后悔了,程序会点击后先不执行等待你发送个undo命令吧。不过如果用在文档编辑的undo功能中应该是挺不错的
- (5)在需要的情况下,可以较容易地将命令记入日志 :也是,每个ConcreteCommand类都是独立的,所以想把命令记录下来是很简单的事情该是我MonkeyKeyEvent的命令总不会变成是你MonkeyTouchEvent的命令嘛
- Monkey源码分析之事件注入
- Monkey源码分析之事件注入
- monkey源码分析之事件注入方法变化
- monkey源码分析之事件注入方法变化
- monkey源码分析之事件注入方法变化(api16之后)
- 【android】Monkey源码分析、事件注入
- Monkey源码分析番外篇之WindowManager注入事件如何跳出进程间安全限制
- Monkey源码分析番外篇之Android注入事件的三种方法比较
- Monkey源码分析番外篇之WindowManager注入事件如何跳出进程间安全限制
- Monkey源码分析番外篇之Android注入事件的三种方法比较
- Monkey源码分析之事件源
- Monkey源码分析之事件源
- UiAutomator源码分析之注入事件
- 第6章7节《MonkeyRunner源码剖析》Monkey原理分析-事件源-事件源概览-注入按键事件实例
- Monkey源码分析之运行流程
- Monkey源码分析之运行流程
- android Monkey测试源码分析之二
- UiAutomator系列之——UiAutomator源码分析之注入事件(004)
- 界面测试(转载)
- 黑马程序员_毕向东_Java基础视频教程第05天
- GPIO输入输出模式配置
- OSI七层与TCP/IP五层网络架构详解
- 求一些数的最高位
- Monkey源码分析之事件注入
- 如何使用 StateServer 保存 Session
- Android中获取Mac地址
- java中如何实现邮件的发送包括网页文件的发送
- SQL AND OR的用法
- 高效程序猿之 VS2010快速生成代码模板
- ITer 你真的有职业规划吗?
- java基础巩固之反射(二)
- 【分数规划】【最优比率环 & 最优比率生成树】poj3621 Sightseeing Cows && poj2728 Desert King