Monkey基本原理及应用学习[1]

Monkey基本原理及应用学习[1]

十一月 14, 2019

Monkey基础知识

Monkey概况

Android官方对于Monkey的描述是这样的:Monkey是Google提供的一个命令行工具,可运行在模拟器或实际设备中。它想系统发送伪随机的用户事件,模拟用户的按键输入、触摸屏输入、手势输入等,从而对正在运行的应用程序进行压力测试,目的是看设备多长时间会出现异常,并观察系统的稳定性和容错性能。

Monkey是Android自带的,其启动脚本位置:/system/bin目录下的Monkey文件;其jar包的位置:/system/framework目录的Monkey.jar文件,用户主要通过adb命令启动Monkey,Monkey在运行时,会根据命令行参数配置,生成伪随机事件流,并在Android设备上执行对应的测试事件。

Monkey参数

Monkey启动的命令行脚本:

1
monkey [options] <count>

options为Monkey执行的可配置参数,为可选项。count为Monkey执行的事件数,为必选项。

Options可划分为五类:

  • 基本配置类参数
  • 事件类型和频率参数
  • 约束限制类参数
  • 调试类参数
  • 官方隐藏类参数

下面是针对五种类型参数的详细介绍:

1.基本配置类参数

Monkey的基本配置类参数包括帮助参数和日志信息参数。帮助参数用于输出Monkey命令使用指导;日志信息参数将日志分为三个等级,级别越高,日志的信息越详细。

参数 说明
-- help 输出Monkey命令行使用方法
-v(Level 0) 标识反馈信息级别,每增加一个-v参数,反馈信息级别会相应提高。只提供启动提示、测试完成和最终结果。
-v -v(Level 1) 提高较为详细测试信息,如逐个发送的Activity事件。
-v -v -v(Level 2) 提供更加详细的测试信息,如测试选中或未选中的Activity事件。

举例:

1
adb shell monkey -v -v 10

2.事件类型和频率参数

Monkey的事件类参数的作用是对随机事件进行调控,从而遵循设定运行。大部分常用的事件类型如下表:

参数 说明
-s <种子值> 伪随机数生成的种子值。如果用相同的种子值再次运行Monkey,将生成相同的事件序列。举例:adb shell monkey -s1000 -v 10
--throttle <毫秒数> 在事件之间插入固定延迟,通过这个可以减缓Monkey的执行速度。
--pct-touch <百分比> 调整触摸事件的百分比
--pct-motion <百分比> 调整动作事件的百分比(这里的动作事件是指直线移动,下面的trackball移动包含曲线移动)
--pct-pinchzoom <百分比> 调整二指缩放事件的百分比
--pct-trackball <百分比> 调整轨迹事件的百分比
--pct-rotation <百分比> 调整屏幕旋转事件的百分比
--pct-nav <百分比> 调整“基本”导航事件的百分比(导航事件由方向输入设备的上下左右按键所触发的事件组成)
--pct-majornav <百分比> 调整“主要”导航事件的百分比(这类导航按键通常引发UI界面的动作,如5-way键盘的中间按键、回退按键、菜单按键)
--pct-syskeys <百分比> 调整“系统”按键事件的百分比(如返回,音量,菜单键等等)
--pct-appswitch <百分比> 调整启动Activity的百分比
--pct-flip <百分比> 调整键盘事件的百分比
--pct-anyevent <百分比> 调整其他事件的百分比

3.约束限制类参数

Monkey的约束限制参数的作用是将随机事件运行的范围限制在一个或多个包或类中。

参数 说明
-p <包名> 此参数用于指定一个或几个包。要指定多个包需要同时使用多个-p选项,每个-p选项只能用来限制一个包。
-c <类别名> 此参数用于指定一个或几个类别。要指定多个类别需要同时使用多个-c选项,每个-c选项只能用来限制一个类别。

4.调试类参数

通过调试命令,对Monkey进行简单的调试,可以快速定位一些Monkey执行过程中的问题。如果用户想监控应用程序所调用的包之间的转换,则可以用 –dbg-no-events参数;如果用户想监控内存泄漏,则可以用 –hprof参数。详细参数如下表

参数 说明
--dbg-no-events 设置此选项,Monkey将初始启动,进入一个测试Activity,不会再进一步生成事件。为了得到最佳结果,把它与-v、一个或几个包约束,以及一个保持Monkey运行30秒或更长事件的非零值联合起来,从而提供一个监视应用程序所调用包之间的转换的环境。
--hprof 设置此选项,将在Monkey执行之前和执行之后生成内存快照文件存放于手机的data/misc目录。可以协助定位内存泄漏问题。由于内存快照文件较大,要小心使用。
--ignore-crashes 通常,Monkey在遇到程序崩溃或异常时,会停止运行。如果设置此选项,Monkey将继续向系统发送事件,直到完成。
--ignore-timeouts 通常,Monkey在遇到超时错误时会停止运行,设置选项后,Monkey将继续运行。
--ignore-security-exceptions 通常,Monkey在遇到许可错误时会停止运行,设置选项后,Monkey将继续运行。
--kill-process-after-error 通常,当Monkey由于一个错误而停止时,出错的应用程序将继续处于运行状态。设置此选项后,将会通知系统停止发送错误的进程。
--monitor-native-crashes 监视并报告Android系统中本地代码的崩溃事件。
--wait-dbg 停止执行中的Monkey,直到有调试器相连。

5.官方隐藏类参数

在Android官网还有三个参数是看不到说明的,即为隐藏参数。

参数 说明
--pkg-blacklist-file <黑名单文件> 限制Monkey不测试指定于黑名单文档中记录的包。该命令可限制Monkey的执行范围。黑名单文档每一行只能存一个包名。
--pkg-whitelist-file <白名单文件> 限制Monkey只测试指定于白名单文档中记录的包。白名单文档每一行只能存一个包名。
-f <脚本文件> 指定Monkey用户执行用户自定义的脚本文件。

Monkey事件

Monkey所执行的随机事件流包含11大事件,分别是触摸事件、手势事件、二指缩放事件、轨迹事件、屏幕旋转事件、基本导航事件、主要导航事件、系统按键事件、启动Activity事件、键盘事件、其他类型事件。

1.触摸事件

该事件可以通过 –pct-touch参数来配置百分比。该事件由一组Touch(ACTION_DOWN)和Touch(ACTION_UP)事件组成,可以在Monkey输出日志中观察到。

2.手势事件

手势事件是指在屏幕某处按下、随机移动、抬起的操作,即直线滑动,可通过 –pct-motion参数来配置百分比。该事件由一个ACTION_DOWN事件、一系列ACTION_MOVE事件和一个ACTION_UP事件组成。

3.二指缩放事件

该事件是指在屏幕上的两处同时按下,并同时移动,最后抬起的操作。可通过 –pct-pinchzoom参数来控制其百分比。该事件是一个ACTION_DOWN事件和一个ACTION_POINTER_DOWN事件,即模拟两个手指同时点下,中间是一系列的ACTION_MOVE事件,即两个手指同时在屏幕上直线滑动;结束是由一个ACTION_POINTER_UP事件和一个ACTION_UP事件组成,即两个手指同时放开。

4.轨迹事件

轨迹事件是由一个或多个随机移动组成的,有时候会伴随着点击。可通过–pct-trackball参数来配置百分比。该事件由一系列的Trackball(ACTION_MOVE)事件组成。

5.屏幕旋转事件

该事件是一个隐藏事件,用于模拟手机的横屏与竖屏的切换。可通过 –pct-rotation参数来配置其事件百分比。该事件由一个rotation事件组成,其中degree表示的是旋转角度,旋转方向为顺时针旋转,0表示旋转90度,1表示旋转180度,2表示旋转270度,3表示旋转360度。

6.基本导航事件

该事件指点击方向输入设备的上、下、左、右按键的操作,目前市面上的手机很少有这类按键了,所以该事件用的很少。可通过 –pct-nav参数来设置百分比。该事件由一个Key(ACTION_DOWN)和一个Key(ACTION_UP)组成的。

7.主要导航事件

指点击“主要导航”按键的操作,这些按键通常会导致UI界面的动作,如菜单键,回退键等。可通过 –pct-majornav参数来配置百分比。该事件由一个Key(ACTION_DOWN)和一个Key(ACTION_UP)组成的。

8.系统按键事件

指点击系统保留使用的按键的操作,如Home键,返回键,音量键等。可通过 –pct-syskeys参数来配置事件的百分比。该事件由一个Key(ACTION_DOWN)和一个Key(ACTION_UP)组成的。

9.启动Activity时间

指在手机上启动一个Activity的操作,在随机时间间隔中,Monkey将执行一个startActivity()的方法。可通过 –pct-appswitch参数来配置时间百分比。该事件由一个Switch操作组成。

10.键盘事件

主要做一些与键盘相关的操作。比如输入框点击、键盘弹起、点击输入外区域、键盘收回等。可通过 –pct-flip参数来配置事件百分比。

11.其他类型事件

该事件是指前面提到的10种事件之外的事件,可通过 –pct-anyevent参数来配置百分比,这个事件一般使用比较少。

Monkey启动

通过命令行启动Monkey有两种方式:

  • 直接PC启动
1
adb shell monkey [options] <count>
  • Shell端启动
1
2
adb shell
monkey [options] <count>

这两种的区别就是,通过PC端启动,Monkey日志可以保存在PC上;通过Shell端启动,Monkey运行日志可以保存在手机里。

注意:停止Monkey的方法:直接杀掉手机上的Monkey进程,具体做法如下:

1.获取到 com.android.commands.monkey的进程ID

1
adb shell ps | grep monkey

2.通过kill命令沙县对应的monkey进程

1
adb shell kill <pid值>

Monkey测试方法

Monkey测试实例

1.常规的稳定性测试

测试脚本示例:

1
adb shell monkey -p com.xxx.xxx --pct-touch 40 --pct-motion 25 --pct-appswitch 10 --pct-rotation 5 -s 12358 --throttle 400 --ignore-crashes --ignore-timeouts -v 500000

1)使用-p参数来限制测试应用的包名

如何查找包名呢?通常有几种方法:

1.通过pm命令查看

1
2
adb shell
pm list package

2.查看APK源码下的AndroidManifest.xml文件

3.通过aapt命令查看

1
aapt dump badging  <后面跟你的apk路径>

4.通过adb logcat抓取当前Android手机运行的APP包名

2)使用–pct-xxx来控制事件发生的百分比

上述脚本可以看到使用了4个 –pct-xxx命令,–pct-touch点击事件的占比调整为40%,–pct-motion手势事件的占比调整为25%, –pct-appswitch Activity切换事件调整为10%,–pct-rotation轨迹事件调整为5%。

3)使用-s参数来指定命令执行的seed(种子)值

Monkey根据seed值来生成对应的事件流,同一个seed生成的事件流是完全相同的。这里指定seed值,是为了测试发现问题时,便于问题的复现。

4)使用 –throttle参数控制Monkey每个操作间时间间隔

为了避免过于频繁的操作导致系统崩溃,因此需要设定一定的间隔时间。

5)使用 –ignore-crashes和 –ignore-timeouts参数使Monkey遇到意外能继续执行

增加此参数为避免在执行测试的过程中不会因为崩溃或超时错误而终止测试。

6)使用 -v指定log的级别

通过增加-v 来增加log的显示信息。

2.自定义脚本的稳定性测试

Monkey自定义脚本的编写模板和代码清单如下:

模板:

1
2
3
4
5
6
7
8
9
10
11
12
#头文件,控制Monkey的参数
#脚本类型,一般不用更改
type =raw events
#脚本执行次数
count=10
#命令执行速率
speed=1.0
#以下为Monkey命令
start data>>
LaunchActivity(pck_name,cl_name)
DispatchPress(KEYCODE_HOME)
.........

清单:

API 说明
LaunchActivity(pkg_name,cl_name) 启动被测应用某个Activity。pkg_name:包名;cl_name:Activity名
Tap(x,y,tapDuration) 模拟一次手指点击事件。x:点击的横坐标;y:点击的纵坐标;tapDuration:按下的时长,单位是ms
DispatchPress(keyName) 模拟按键点击。keyName:案件的名称
RotateScreen(rotationDegree,peresist) 模拟屏幕旋转。rotationDegree:用0~3分别表示顺时针旋转的四个方向;peresist:是否存留
DispatchFlip(true/false) 打开或关闭软键盘。
LongPress() 长按两秒。
PressAndHold(x,y,pressDuration) 模拟长按事件。x:点击横坐标;y:点击纵坐标;pressDuration:点击时长,单位是ms
DIspatchString(input) 输入字符串。
Drag(xStart,yStart,xEnd,yEnd,stepCount) 模拟拖动操作。xStart:起始横坐标;yStart:起始纵坐标;xEnd:结束横坐标;yEnd:结束纵坐标;stepCount:移动的速度
PinchZoom(pt1xStart,pt1yStart,pt1xEnd,pt1yEnd,p2xStart,p2yStart,p2xEnd,p2yEnd,stepCount) 模拟缩放手势。
UserWait(sleepTime) 设置等待时间。sleepTime:等待时间,单位是ms。
DeviceWakeUp() 唤醒屏幕。

将编写好的脚本存储为后缀为.script的文件,并且将文件push到手机或模拟器的/sdcard/路径下:

1
adb push monkey.script  /sdcard/

执行脚本:

1
adb shell monkey -f /sdcard/monkey.script -v 1

就可以运行Monkey脚本了。

3.结合辅助命令,获取更多的信息

  • 获取logcat日志信息:
1
adb shell logcat -v time>log.txt
  • 获取内存信息:
1
adb shell dumpsys meminfo <进程名>

获取CPU消耗信息:

1
adb shell top -n 1 | find "进程名"
  • 获取电量信息:
1
adb shell dumpsys battery
  • 获取GPU信息:
1
adb shell dumpsys gfxinfo <进程名>
  • 获取流量信息:
1
adb shell cat /proc/uid_stat/<被测应用uid>/tcp_rcv

如何获取应用的uid呢?下面来看看:

(1)查看被测应用的进程ID(PID):

1
adb shell ps | grep <被测应用的包名>

(2)查看应用的用户ID(UID):

1
adb shell cat /proc/$pid/status

Monkey日志分析

1.Monkey日志保存的方法

保存的方法有三种,如下:

  • 保存在PC上:
1
adb shell monkey [options] <count> >d:\monkey.txt
  • 保存在手机上,代码如下:
1
2
adb shell
monkey [options] <count> > /mnt/sdcard/monkey.txt
  • 标注流与错误流分开保存,代码如下:
1
Monkey [options] <count> 1>/sdcard/monkey.txt 2>/sdcard/error.txt

2.Monkey日志分析

Monkey日志分析bat脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@echo off&setlocal enabledelayedexpansion
: 设置所用Monkey日志存放目录
set ff=log\*.txt
: 设置查询关键字
set str=CRASH crash ANR died
: 设置查询结果存放的目录
set fileName=Result.txt
: 开始查询
echo 正在统计&echo;
echo %date% %time% > %fileName%
echo .>>%fileName%
echo 分析结果:>>%fileName%
echo -------------------------------------------->>%fileName%
: 依次打开目录下的每一个Monkey日志查询关键字并输出个数
(for %%a in (%str%)do (
set n%%a=0&set/p= %%a : <null>con
for /f "delims=" %%b in ('findstr"%%a" "%ff%"')do (
set h=%%b
call :yky %%a)
echo !n%%a!>com
echo 关键字 %%a 共有 !%%a!
))>>%fileName%
echo .>>%filename%
: 针对崩溃的日志输出其所在文件行数
echo 崩溃日志: >>%fileName%
findstr "%str%" "%ff%">>%fileName%
echo /&pause&exit
:yky
set /a n%1+=1
set h=!h:*%1=!
if defined h if not "!h:*%1=!"=="!h!" goto :yky

执行脚本后可以得到crash和安然出现的次数,再打开对应日志进行查找,这里给出了一些crash的错误信息:

Crash关键词 Crash原因
java.lang.NullPointerException 空指针异常
java.lang.ArrayIndexOutOfBoundsException 数组溢出
java.lang.ClassNotFoundException 类不存在
java.lang.ArithmeticException 数学运算异常
java.lang.IllegalArgumentException 方法参数异常
java.io.FileNotFoundException 文件未找到
java.lang.NumberFormatException 数值转化异常
java.lang.StackOverflowError 堆栈异常错误
java.lang.OutOfMemoryError 内存溢出错误

当获取到Crash和ANR日志信息后,理论上可以根据日志信息去定位问题,但是只靠日志去定位还是很困难的,有时候还需要知道问题出现的场景以及操作步骤,通常,可以通过使用speed值再一次执行Monkey来复现问题,但这种方法比较费时,那么问题来了,Monkey出现问题的时候有没有可能及时的截图并记录下操作步骤呢?可以通过Monkey改造来实现,当然了,这个问题放在后面详细讨论。