Android之Monkey源码分析(第十三篇:触摸事件流程分析)

2023-10-23 13:04

本文主要是介绍Android之Monkey源码分析(第十三篇:触摸事件流程分析),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

    前面讲了一些monkey作者的设计思想(有的我没写完,还没发布,惨),这篇来点实际的,monkey程序是如何发起一个触摸事件的呢?

本例子中假设使用的命令是:

adb shell monkey -p com.android.camera1000

表示向相机app发出1000个事件,所有事件都是随机的,其中会包括touch事件,那么这些touch事件是怎么构造的呢?

执行流程

一、入口函数

    public static void main(String[] args) {//…………省略……int resultCode = (new Monkey()).run(args); //……………省略……}

main函数是java程序的入口函数,monkey作为一个标准的java程序,当然它也最先调用main函数,在main函数的内部,创建了Monkey对象,并调用了run()方法,参数args表示命令行参数数组,同时也传了进去

二、Monkey的run()方法

    private int run(String[] args) {//………………省略…………mEventSource = new MonkeySourceRandom(mRandom, mMainApps,mThrottle, mRandomizeThrottle, mPermissionTargetSystem); //………………省略…………crashedAtCycle = runMonkeyCycles(); //…………………省略………}

Monkey中的run()方法挺长的,有200多行,该方法完整的分析,可以看我之前的文章,这里我们最需要知道的是两个

1、创建事件来源对象

这里创建的是:MonkeySourceRandom对象

2、调用runMonkeyCycles()方法

调用runMonkeyCycles()方法后,主线程会在该方法内循环执行,当中有我们的touch事件

三、Monkey的runMonkeyCycles()方法

    private int runMonkeyCycles() {//…………省略……while (!systemCrashed && cycleCounter < mCount) {//………………省略……if (shouldAbort) { //………………省略……return eventCounter; }//…………………省略……MonkeyEvent ev = mEventSource.getNextEvent();if (ev != null) {  int injectCode = ev.injectEvent(mWm, mAm, mVerbose);//……………………省略……}//……………………省略……}

runMonkeyCycles()方法也挺长的,截取了今天我们关注的业务逻辑

1、循环获取事件

通过while循环,一直获取事件

2、循环结束条件

第一是:systemCrashed 表示系统发生故障,monkey不再获取新的事件

第二是:cycleCount 超过预设的次数,就是我们在命令行参数传入的事件总数

第三是:shouldAbort 标志位,跟我们命令行传入的命令有关,比如anr、native crash、默认不开启的,这个标志位也会终止事件循环

3、反复获取事件

                MonkeyEvent ev = mEventSource.getNextEvent();

通过之前的文章可知,MonkeyEvent是所有事件类的父类,而这里的mEventSource指向的是MonkeySourceRandom对象,调用它的getNextEvent()方法,这个方法会返回一个Event对象,我们要看到的touch事件,就会在这个方法中返回,我们接下来就去这个方法中看看什么时候会返回touch事件

4、开始注入事件

int injectCode = ev.injectEvent(mWm, mAm, mVerbose);

 每个Event对象会有injectEvent()方法,同时会wms、ams、以及一个debug参数mVerbose传入进去,具体的事件执行,就是在各个事件对象的injectEvent()方法中进行的,稍后我们会看到touch事件的injectEvent是如何发出一个touch事件的!

四、MonkeySourceRandom的getNextEvent()方法

    public MonkeyEvent getNextEvent() {if (mQ.isEmpty()) {   //当双向链表中没有元素时,说明没有可用的事件generateEvents(); //构造事件,可能构造一个,也可能构造多个,构造的事件对象会添加到m}mEventCount++; //MonkeySourceRandom对象持有的事件数量增加1,表示已经提取出的事件数量MonkeyEvent e = mQ.getFirst(); //获取双向链表中的第一个元素,赋值给局部变量e保存mQ.removeFirst(); //删除掉该事件(第一个元素)return e; //向调用者返回MonkeyEvent对象}

getNextEvent()方法不长,但是信息量不错,mQ是个双向链表,用于保存每个事件对象,两种情况

1、mQ中没有事件

mQ.isEmpty()位true,表示没有事件,此时会调用genrateEvents()方法构造事件,我们的touch事件就是在这个方法里构建,接下来就去这个

2、如果mQ中有事件

取出来链表中保存的第一个事件对象

最后总会返回一个事件对象e

五、MonkeySourceRandom的generateEvents()方法

    private void generateEvents() {float cls = mRandom.nextFloat(); int lastKey = 0; if (cls < mFactors[FACTOR_TOUCH]) { generatePointerEvent(mRandom, GESTURE_TAP); //构造Pointer事件(点击事件)return; }// ………………省略…………  } 

这是根据命令行中的比例,构造各类事件的方法,touch事件是最先判断的,它的默认比例(当我们不指定比例,monkey会有默认touch事件比例)

1、随机数

mRandom.nextFloat()会产生一个float型的随机数,mFactors是个数组,它存储着每个事件的比例,touch事件对应的数组下标是FACTOR_TOUCH

2、构造一个点事件

通过调用generatePointerEvent()方法来构造touch事件

六、MonkeySourceRandom的generatePointerEvent()方法

    private void generatePointerEvent(Random random, int gesture) {//………………省略……mQ.addLast(new MonkeyTouchEvent(MotionEvent.ACTION_DOWN) //此处传入的按下的动作.setDownTime(downAt) //记录按下的时间戳.addPointer(0, p1.x, p1.y) //id都传0……,把获取到的x坐标与y坐标也传进去.setIntermediateNote(false)); //false表示这不是一个过渡事件//向双向链表中添加事件,添加一个元素,即MonkeyTouchEvent对象//………………省略……mQ.addLast(new MonkeyTouchEvent(MotionEvent.ACTION_UP).setDownTime(downAt) //为啥还记录的按下的时间?.addPointer(0, p1.x, p1.y).setIntermediateNote(false)); //最后再添加一个ACTION_UP事件,如果是点事件,则至少添加了两个元素对象到mQ中,一个ACTION_DOWN、一个ACTION_UP、并且不是过渡事件}

 touch事件,会向mQ中传入两个元素,一个down表示按下、另一个是up表示抬起,组合在一起就是touch事件

mQ中有了事件,事件就可以被获取到了,接下来事件的injectEvent()方法会被调用了 

由于mQ中实际持有的是MonkeyTouchEvent对象,所以它的injectEvent()方法会被调用

七、MonkeyTouchEvent的injectEvent()方法

如果你是第一次进入MonkeyTouchEvent,你会发现它根本没有injectEvent()方法,这个时候我们要向上查找,去它的父类MonkeyMotionEvent中在找找,这里要注意了

八、MonkeyMotionEvent的injectEvent()方法

    public int injectEvent(IWindowManager iwm, IActivityManager iam, int verbose) {//…………省略……try {if (!InputManager.getInstance().injectInputEvent(me,  //走到这里才是真的向手机注入事件,通过InputManager的injectInputEvent注入事件,传入MotionEvent对象InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT)) { //依赖InputManagerService系统服务注入事件,看来还得死磕Android的系统服务return MonkeyEvent.INJECT_FAIL; //只要IMS返回的是失败,则证明注入失败,看来这里也是同步方法,Monkey主线程会等待执行完……}} finally {me.recycle(); //将缓存的MotionEvent对象回收掉,牛逼!}return MonkeyEvent.INJECT_SUCCESS; //走到这里说明注入事件成功,系统封装好了,靠你来执行了}

我们只关注与事件注入相关的内容,重点来了,injectEvent()方法中并没有使用传入的wms系统服务、也么有使用ams系统服务,而是另外使用了一个ims系统服务

InputManager对象,这个InputManger对象是用来操作InputManagerService系统服务

调用ims系统服务的injectInputEvent()方法,发出touch事件

总结

1、monkey作者熟悉Android框架,它巧妙的使用了系统预留的系统服务,InputMangerService发出了touch事件

2、InputMangerService预留的injectEvent()方法可以发出touch事件,只要熟悉这个方法的使用,我们自己也能写个monkey程序

这篇关于Android之Monkey源码分析(第十三篇:触摸事件流程分析)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/268017

相关文章

SpringBoot整合Flowable实现工作流的详细流程

《SpringBoot整合Flowable实现工作流的详细流程》Flowable是一个使用Java编写的轻量级业务流程引擎,Flowable流程引擎可用于部署BPMN2.0流程定义,创建这些流程定义的... 目录1、流程引擎介绍2、创建项目3、画流程图4、开发接口4.1 Java 类梳理4.2 查看流程图4

java Long 与long之间的转换流程

《javaLong与long之间的转换流程》Long类提供了一些方法,用于在long和其他数据类型(如String)之间进行转换,本文将详细介绍如何在Java中实现Long和long之间的转换,感... 目录概述流程步骤1:将long转换为Long对象步骤2:将Longhttp://www.cppcns.c

MyBatis Plus 中 update_time 字段自动填充失效的原因分析及解决方案(最新整理)

《MyBatisPlus中update_time字段自动填充失效的原因分析及解决方案(最新整理)》在使用MyBatisPlus时,通常我们会在数据库表中设置create_time和update... 目录前言一、问题现象二、原因分析三、总结:常见原因与解决方法对照表四、推荐写法前言在使用 MyBATis

Python主动抛出异常的各种用法和场景分析

《Python主动抛出异常的各种用法和场景分析》在Python中,我们不仅可以捕获和处理异常,还可以主动抛出异常,也就是以类的方式自定义错误的类型和提示信息,这在编程中非常有用,下面我将详细解释主动抛... 目录一、为什么要主动抛出异常?二、基本语法:raise关键字基本示例三、raise的多种用法1. 抛

github打不开的问题分析及解决

《github打不开的问题分析及解决》:本文主要介绍github打不开的问题分析及解决,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、找到github.com域名解析的ip地址二、找到github.global.ssl.fastly.net网址解析的ip地址三

Mysql的主从同步/复制的原理分析

《Mysql的主从同步/复制的原理分析》:本文主要介绍Mysql的主从同步/复制的原理分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录为什么要主从同步?mysql主从同步架构有哪些?Mysql主从复制的原理/整体流程级联复制架构为什么好?Mysql主从复制注意

java -jar命令运行 jar包时运行外部依赖jar包的场景分析

《java-jar命令运行jar包时运行外部依赖jar包的场景分析》:本文主要介绍java-jar命令运行jar包时运行外部依赖jar包的场景分析,本文给大家介绍的非常详细,对大家的学习或工作... 目录Java -jar命令运行 jar包时如何运行外部依赖jar包场景:解决:方法一、启动参数添加: -Xb

Android学习总结之Java和kotlin区别超详细分析

《Android学习总结之Java和kotlin区别超详细分析》Java和Kotlin都是用于Android开发的编程语言,它们各自具有独特的特点和优势,:本文主要介绍Android学习总结之Ja... 目录一、空安全机制真题 1:Kotlin 如何解决 Java 的 NullPointerExceptio

Apache 高级配置实战之从连接保持到日志分析的完整指南

《Apache高级配置实战之从连接保持到日志分析的完整指南》本文带你从连接保持优化开始,一路走到访问控制和日志管理,最后用AWStats来分析网站数据,对Apache配置日志分析相关知识感兴趣的朋友... 目录Apache 高级配置实战:从连接保持到日志分析的完整指南前言 一、Apache 连接保持 - 性

Linux中的more 和 less区别对比分析

《Linux中的more和less区别对比分析》在Linux/Unix系统中,more和less都是用于分页查看文本文件的命令,但less是more的增强版,功能更强大,:本文主要介绍Linu... 目录1. 基础功能对比2. 常用操作对比less 的操作3. 实际使用示例4. 为什么推荐 less?5.