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

news2024/12/26 9:19:23

前言

    前面讲了一些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程序

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1123573.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

公众号留言功能怎么使用?如何开启?

为什么公众号没有留言功能&#xff1f;根据要求&#xff0c;自2018年2月12日起&#xff0c;新申请的微信公众号默认无留言功能。有些人听过一个说法&#xff1a;公众号粉丝累计到一定程度或者原创文章数量累计到一定程度就可以开通留言功能。其实这个方法是2018年之前才可以&am…

每日一题 2316. 统计无向图中无法互相到达点对数(中等,图连通分量)

题目很简单&#xff0c;只要求出每个连通分量有多少个节点即可首先通过建立一个字典来表示每个节点的邻接关系遍历每个节点&#xff0c;并通过邻接关系标记在当前连通分量内的所有的点&#xff0c;这样就可以知道一个连通分量内有多少个点在这里我陷入了一个误区&#xff0c;导…

【Linux】32条指令带你玩转 Linux !

目录 1&#xff0c;whoami 2&#xff0c;who 3&#xff0c;pwd 4&#xff0c;ls 1&#xff0c;ls 2&#xff0c;ls -l 3&#xff0c;ls -a 4&#xff0c;ls -al 5&#xff0c;ls -d 6&#xff0c;ls -ld 5&#xff0c;clear 6&#xff0c;cd 1&#xff0c;cd 2&…

看得懂的——数据库中的“除”操作

通过一个例子来解释数据库中的“除”操作 R➗S其实就是判断关系R中X各个值的象集Y是否包含关系S中属性Y的所有值 求解步骤 第一步 找出关系R和关系S中相同的属性&#xff0c;即Y属性。在关系S中对Y做投影&#xff08;即将Y列取出&#xff09;&#xff1b;所得结果如下&#x…

掌握测评补单技术对Shopee、Lazada店铺有什么好处?

虾皮(Shopee)、lazada作为东南亚地区最大的电商平台之一&#xff0c;吸引了众多卖家加入其平台&#xff0c;竞争激烈。在如此庞大的市场中&#xff0c;如何优化你的shopee、lazada店铺商品再结合自养号测评&#xff0c;提高曝光率和销售能力成为关键。本文将分享一些有效的方法…

解决dirsearch扫描工具pkg_resources模块警告问题

一、pkg_resources模块问题 ┌──(kali㉿kali)-[~/桌面/XXX/dirsearch-master] └─$ python dirsearch.py -h /home/kali/XX/XXXX/dirsearch-master/dirsearch.py:23: DeprecationWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io…

VMware Workstation安装ESXi和vCenter(8.0)

一、环境准备 虚拟机&#xff1a;VMware Workstation 17 Pro ESXi&#xff1a;ESXi-8.0U2-22380479-standard vCenter&#xff1a;VMware-VCSA-all-8.0.2-22385739.iso 主要是内存设置&#xff0c;因为vCenter需要14Gb内存&#xff0c;所以这个至少16Gb。 硬盘需要2块&…

【漏洞复现】蓝凌EIS智慧协同平台saveImg接口存在任意文件上传

漏洞描述 蓝凌智慧协同平台满足组织企业在知识、协同及项目管理系统中建设等需求。该平台在saveImg接口处存在任意文件上传,攻击者可通过该漏洞上传Weshell控制服务器。 免责声明 技术文章仅供参考,任何个人和组织使用网络应当遵守宪法法律,遵守公共秩序,尊重社会公德,…

65%更小的APK和70%更少的内存:如何优化我的Android App的内存

65%更小的APK和70%更少的内存&#xff1a;如何优化我的Android App的内存 (Note: This is a translation of the provided title) 为什么应用程序内存很重要&#xff1f; 使用最少的内存的高效应用程序可以提升性能&#xff0c;节省设备资源并延长电池寿命。它们提供流畅的用…

学生台灯选什么光源好?适合学生的护眼台灯推荐

现在的台灯可以说是孩子必不可少的一个学习灯具了&#xff0c;几乎每家每户都会备着一台。不过台的好坏也有区别&#xff0c;相对而言&#xff0c;以前所用的白炽灯、日光灯、节能灯等等传统台灯已经是不适合孩子使用的了&#xff0c;目前而言最好的是LED灯。下面小编为大家推荐…

【消费战略】解读100个食品品牌|意面突起,“空刻”的品类心智占位!

空刻意面&#xff0c;一个开创意大利面速食化的新消费品牌&#xff0c;凭借着核心大单品意大利面&#xff0c;在过去短短的几年中&#xff0c;获得不俗的市场成绩和品牌影响力&#xff0c;占领了空刻意面的消费心智&#xff1a; 2019年&#xff0c;AIRMETER氢刻意面上线天猫旗舰…

急需一个可以短视频配音的手机App~

很多小伙伴视频拍摄得很好&#xff0c;但不想用自己的声音做解说旁白&#xff0c;怎么办&#xff1f;这时候就急需一个可以配音的手机App&#xff01;今天给大家推荐一款一款专注于文字转语音的智能语音合成工具——悦音配音。声音堪比真人发声的配音服务平台&#xff0c;里面拥…

在 Windows 10/11 上恢复已删除文件的 9 种简单方法

本教程讨论永久丢失数据的原因以及在 Windows 10/11 上恢复已删除文件的不同方法&#xff1a; 数据是提供给系统的任何形式的信息。它可以是从密码到记事本文件的任何内容。数据是当今世界的关键要素&#xff0c;因为它使我们的生活变得轻松。 我们每天都变得越来越依赖数据&…

如何禁止别人调试自己的前端代码?

在前端开发中&#xff0c;无法完全禁止别人对自己的前端代码进行调试&#xff0c;因为前端代码在客户端执行&#xff0c;而客户端环境是可被用户访问和控制的。然而&#xff0c;可以采取一些措施来增加代码的安全性和复杂度&#xff0c;使得调试和代码分析变得更加困难。以下是…

橡胶软管在气密测试时如何保持管口的形状不变

在汽车整机或者零部件中&#xff0c;我们经常能看到各种各样的橡胶软管&#xff0c;主要负责导通各种冷却液、油等&#xff0c;是汽车正常工作不可分割的一部分。为了确保软管在使用前具有良好的密封性能&#xff0c;出厂前的气密性测试可以检测软管是否存在漏气或渗漏的问题&a…

新零售系统主要功能有哪些?新零售系统开发公司推荐

新零售系统是一套全面的数字化解决方案&#xff0c;旨在帮助实体零售店提升运营效率、优化用户体验并实现持续增长。以下是新零售系统的主要功能&#xff1a; l 用户画像&#xff1a;系统通过收集和分析顾客的行为、偏好、购买历史等数据&#xff0c;构建出完整的用户画像。这…

智加科技与东风柳汽达成深度合作 自动驾驶重卡计划2024年初量产交付

&#xff08;2023年10月19日&#xff0c;苏州&#xff09;全球领先的重卡自动驾驶技术公司智加科技与东风柳汽宣布&#xff0c;双方共同开发的自动驾驶重卡H7计划2024年初实现量产交付。未来&#xff0c;双方将携手推出安全可靠、高性价比、性能卓越的自动驾驶重卡产品&#xf…

Java编译多个目录下的文件

编译单个目录下的Java文件 javac -d <放置输出类文件的目录> <源文件的目录>/*.java 例如&#xff0c;在src/com/thb目录下有两个java文件&#xff1a; 执行编译命令javac -d D:\temp\outputdata src/com/thb/*.java可以编译这个目录下的两个java文件&#xff…

华为云HECS安装docker-compose

Docker Compose是一个用来定义和运行多个复杂应用的Docker编排工具。例如&#xff0c;一个使用Docker容器的微服务项目&#xff0c;通常由多个容器应用组成。那么部署时如何快速启动各个微服务呢&#xff0c;一个个手动启动&#xff1f;假如有上百个微服务呢&#xff0c;显然不…

python 打包 pyinstall

一、PyInstaller介绍 一个打包工具&#xff0c;可将Python代码打包成单个可执行文件&#xff0c;支持Windows、macOS和Linux等多个平台&#xff0c;可以将依赖包和资源文件一起打包。 二、PyInstaller安装及使用方法 1.PyInstaller安装 在终端执行&#xff1a; pip instal…