android monkey原理_Android 性能测试之 Monkey
这里简单说一下monkey的实现原理。
起步
当你执行adb shell monkey的时候,它到底干了什么。
monkey位于/system/bin目录下。内容为:
# Script to start "monkey" on the device, which has a very rudimentary
# shell.
#
base=/system
export CLASSPATH=$base/framework/monkey.jar
trap "" HUP
exec app_process $base/bin com.android.commands.monkey.Monkey $*
app_process是Android的系统启动进程,用于启动zygote和其他java进程:
if (zygote) {
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (className) {
runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
}
更详细的内容,需要阅读android源码,这里不做详细扩展。
adb这里是runtime执行com.android.internal.os.RuntimeInit来启动,位置在:
/system/framework/下面。有很多系统的包,其中有一个/system/framework/monkey.jar为monkey的所在包。
Application that injects random key events and other actions into the system.
下面,我们一步一步讲解一下:
public static void main(String[] args) {
// Set the process name showing in "ps" or "top"
Process.setArgV0("com.android.commands.monkey");
int resultCode = (new Monkey()).run(args);
System.exit(resultCode);
}
看一下run具体方法:
monkey中注入系统事件是通过使用内部API来实现的(activemanger, windowmanger, packagemanger),其他方式(instrumentation)只能是二等公民。
private int run(String[] args) {
processOptions();//处理参数
loadPackageLists();//加载黑白名单,可测的有效包名
getSystemInterfaces();//获取系统接口,都是系统的隐藏接口。
//mAm = ActivityManagerNative.getDefault();
//这里返回了一个ActivityManagerProxy对象,用来执行mangerservice接口。
//mWm = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
//上面,获取了系统窗口服务
//mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
getMainApps();//获取要执行的activity
mEventSource = new MonkeySourceRandom(mRandom, mMainApps,
mThrottle, mRandomizeThrottle, mPermissionTargetSystem);//产生一个随机事件
((MonkeySourceRandom) mEventSource).setFactors(i, mFactors[i]);
mEventSource.validate();//验证事件,并调整比例
mNetworkMonitor.start();//监听网络变化
crashedAtCycle = runMonkeyCycles();//monkey核心逻辑
}
我们看一下monkey的事件列表类:
public abstract class MonkeyEvent {
protected int eventType;
public static final int EVENT_TYPE_KEY = 0;
public static final int EVENT_TYPE_TOUCH = 1;
public static final int EVENT_TYPE_TRACKBALL = 2;
public static final int EVENT_TYPE_ROTATION = 3; // Screen rotation
public static final int EVENT_TYPE_ACTIVITY = 4;
public static final int EVENT_TYPE_FLIP = 5; // Keyboard flip
public static final int EVENT_TYPE_THROTTLE = 6;
public static final int EVENT_TYPE_PERMISSION = 7;
public static final int EVENT_TYPE_NOOP = 8;
public static final int INJECT_SUCCESS = 1;
public static final int INJECT_FAIL = 0;
// error code for remote exception during injection
public static final int INJECT_ERROR_REMOTE_EXCEPTION = -1;
// error code for security exception during injection
public static final int INJECT_ERROR_SECURITY_EXCEPTION = -2;
public MonkeyEvent(int type) {
eventType = type;
}
...
monkey有11种事件,在MonkeyEventSource中有事件的比例设置。
下面,我们来看monekey的核心执行逻辑;
while (!systemCrashed && cycleCounter < mCount) {
//检查是否发生了ANR
if (mRequestAnrBugreport){
getBugreport("anr_" + mReportProcessName + "_");
mRequestAnrBugreport = false;
}
//检查系统watchdog是否报告bug
if (mRequestWatchdogBugreport) {
System.out.println("Print the watchdog report");
getBugreport("anr_watchdog_");
mRequestWatchdogBugreport = false;
}
//检查是否发生了CRASH
if (mRequestAppCrashBugreport){
getBugreport("app_crash" + mReportProcessName + "_");
mRequestAppCrashBugreport = false;
}
//检查bugreport报告生成
if (mRequestPeriodicBugreport){
getBugreport("Bugreport_");
mRequestPeriodicBugreport = false;
}
//报告系统信息,ANR时出发
if (mRequestDumpsysMemInfo) {
mRequestDumpsysMemInfo = false;
shouldReportDumpsysMemInfo = true;
}
//获取下一个随机时间
MonkeyEvent ev = mEventSource.getNextEvent();
//注入事件
int injectCode = ev.injectEvent(mWm, mAm, mVerbose);
}
回到之前的代码逻辑,这个mEventSource有三种来源:
//脚本模式
mEventSource = new MonkeySourceScript(mRandom, mScriptFileNames.get(0), mThrottle,
mRandomizeThrottle, mProfileWaitTime, mDeviceSleepTime);
mEventSource = new MonkeySourceRandomScript(mSetupFileName,
mScriptFileNames, mThrottle, mRandomizeThrottle, mRandom,
mProfileWaitTime, mDeviceSleepTime, mRandomizeScript);
//网络模式,monkeyrunner的使用方式
mEventSource = new MonkeySourceNetwork(mServerPort);
//默认模式,一般都使用随机事件
mEventSource = new MonkeySourceRandom(mRandom, mMainApps,
mThrottle, mRandomizeThrottle, mPermissionTargetSystem);
好,我们这里展开说一下脚本模式怎么使用monkey.先写一个简单的monkey事件脚本文件:
/**
* monkey event queue. It takes a script to produce events sample script format:
*
*
* type= raw events
* count= 10
* speed= 1.0
* start data >>
* captureDispatchPointer(5109520,5109520,0,230.75429,458.1814,0.20784314,0.06666667,0,0.0,0.0,65539,0)
* captureDispatchKey(5113146,5113146,0,20,0,0,0,0)
* captureDispatchFlip(true)
* ...
*
*/
#我们以小米商城为例,进入商城,滑动到最下面
type= user
count= 49
speed= 1.0
start data >>
LaunchActivity(com.xiaomi.shop, com.xiaomi.shop.activity.MainTabActivity)
#wait for launch
UserWait(10000)
#drag to down
Drag(542,1326,542,560,15)
#wait for 500 milliseconds
UserWait(500)
#tap second tab
Tap(346,1868)
那这个脚本是怎么解析的呢?(这里不详细展开):
readHeader();//打开文件,读文件头,设置参数,文件头的结尾必须是:STARTING_DATA_LINE
当然,脚本中也可以不写文件头的。
readLines();
readNextBatch();
processLine();//处理每一行命令,加入事件队列中。命令包括:
```java
// event key word in the capture log
private static final String EVENT_KEYWORD_POINTER = "DispatchPointer";
private static final String EVENT_KEYWORD_TRACKBALL = "DispatchTrackball";
private static final String EVENT_KEYWORD_ROTATION = "RotateScreen";
private static final String EVENT_KEYWORD_KEY = "DispatchKey";
private static final String EVENT_KEYWORD_FLIP = "DispatchFlip";
private static final String EVENT_KEYWORD_KEYPRESS = "DispatchPress";
private static final String EVENT_KEYWORD_ACTIVITY = "LaunchActivity";
private static final String EVENT_KEYWORD_INSTRUMENTATION = "LaunchInstrumentation";
private static final String EVENT_KEYWORD_WAIT = "UserWait";
private static final String EVENT_KEYWORD_LONGPRESS = "LongPress";
private static final String EVENT_KEYWORD_POWERLOG = "PowerLog";
private static final String EVENT_KEYWORD_WRITEPOWERLOG = "WriteLog";
private static final String EVENT_KEYWORD_RUNCMD = "RunCmd";
private static final String EVENT_KEYWORD_TAP = "Tap";//点击,轻触
private static final String EVENT_KEYWORD_PROFILE_WAIT = "ProfileWait";
private static final String EVENT_KEYWORD_DEVICE_WAKEUP = "DeviceWakeUp";
private static final String EVENT_KEYWORD_INPUT_STRING = "DispatchString";
private static final String EVENT_KEYWORD_PRESSANDHOLD = "PressAndHold"; //
private static final String EVENT_KEYWORD_DRAG = "Drag"; //拖动
private static final String EVENT_KEYWORD_PINCH_ZOOM = "PinchZoom";
private static final String EVENT_KEYWORD_START_FRAMERATE_CAPTURE = "StartCaptureFramerate";
private static final String EVENT_KEYWORD_END_FRAMERATE_CAPTURE = "EndCaptureFramerate";
private static final String EVENT_KEYWORD_START_APP_FRAMERATE_CAPTURE =
"StartCaptureAppFramerate";
private static final String EVENT_KEYWORD_END_APP_FRAMERATE_CAPTURE = "EndCaptureAppFramerate";
就是酱紫。执行一下我们的脚本(命令列表):
adb -s 8b52f091 push d:\script.txt /sdcard/data
monkey -f /sdcard/data/script.txt 1
你可以看到,我们滑动到了底部,然后打开了第二个TAB。当然,我们可以直接通过adb shell来执行上面的操作:
adb shell input swipe 542 1326 560 15
adb shell input swipe 542 1326 560 15
adb shell input tap 346 1868
这里使用的是input命令来执行。和monkey一样,input是一个脚本,执行的是/system/framework/input.jar:
$ cat /system/bin/input
# Script to start "input" on the device, which has a very rudimentary
# shell.
#
base=/system
export CLASSPATH=$base/framework/input.jar
exec app_process $base/bin com.android.commands.input.Input "$@"
回到monkey上去,上面说到
int injectCode = ev.injectEvent(mWm, mAm, mVerbose);
这个事件来源三类,我们现在看默认的随机事件(MonkeySourceRandom),它的getnextevent返回多种随机事件,这里以MonkeyMotionEvent为例进行说明
@Override
public int injectEvent(IWindowManager iwm, IActivityManager iam, int verbose) {
MotionEvent me = getEvent();
if ((verbose > 0 && !mIntermediateNote) || verbose > 1) {
StringBuilder msg = new StringBuilder(":Sending ");
msg.append(getTypeLabel()).append(" (");
switch (me.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
msg.append("ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
msg.append("ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
msg.append("ACTION_UP");
break;
case MotionEvent.ACTION_CANCEL:
msg.append("ACTION_CANCEL");
break;
case MotionEvent.ACTION_POINTER_DOWN:
msg.append("ACTION_POINTER_DOWN ").append(me.getPointerId(me.getActionIndex()));
break;
case MotionEvent.ACTION_POINTER_UP:
msg.append("ACTION_POINTER_UP ").append(me.getPointerId(me.getActionIndex()));
break;
default:
msg.append(me.getAction());
break;
}
msg.append("):");
int pointerCount = me.getPointerCount();
for (int i = 0; i < pointerCount; i++) {
msg.append(" ").append(me.getPointerId(i));
msg.append(":(").append(me.getX(i)).append(",").append(me.getY(i)).append(")");
}
System.out.println(msg.toString());
}
try {
//InputManager.getInstance返回input manager的实例
//Injects an input event into the event system on behalf of an application
//注入事件
if (!InputManager.getInstance().injectInputEvent(me,
InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT)) {
return MonkeyEvent.INJECT_FAIL;
}
} finally {
me.recycle();
}
return MonkeyEvent.INJECT_SUCCESS;
}
其他如MonkeyRotationEvent,使用iwm.freezeRotation(mRotationDegree);来实现旋转屏幕。
总结monkey事件来源三种:默认随机事件、脚本定义事件、network网络事件;
monkey事件根据类型比例生成事件队列,循环查找事件;
monkey事件的实现使用系统内部API(activemanager,inputmanager,windowmanager)来实现;
android monkey原理_Android 性能测试之 Monkey相关推荐
- android crash没有日志_App测试之monkey(四)-调试参数及日志
由于monkey在测试app时,我们需要作长时间的稳定性测试,比如连续测试10小时(monkey不能指定时间,可以指定次数,时间可以在测试次数的日志基础上大概算出来),在测试过程中,app很可能测试时 ...
- android monkey原理_Android Monkey原理探讨-阿里云开发者社区
0x0 概述 本文不涉及如何使用monkey,官网说得够详细了,网上资料也一大堆.本文着重探讨monkey的实现原理,以及基于这些原理,我们可以做些什么?本文涉及的Monkey的源码位于AOSP的de ...
- monkey测试_安卓测试之monkey
安卓手机最简单的随机压力测试工具monkey真的是测试的福星,人人都可以用,只需要敲一串命令即可: 举例:adb shell monkey -p com.android.camera --ignore ...
- Android Studio 忽略_Android性能优化--启动优化
1. 前言 一个应用App的启动速度能够影响用户的首次体验,启动速度较慢(感官上)的应用可能导致用户再次开启App的意图下降,或者卸载放弃该应用程序.本文会通过以下几个方面来介绍应用启动的相关指标和优 ...
- Monkey原理初步和改良优化--Android自动化测试学习历程
章节:自动化基础篇--Monkey原理初步和改良优化(第三讲) 主要讲解内容与笔记: 一.理论知识: 直接看文档,来了解monkey的概念.基本原理,以及如何使用. First,what is And ...
- [Android 测试] 压力稳定性测试之: Monkey 详解分析脚本
一.什么是稳定性测试? 通过随机点击屏幕一段时间,看看app会不会奔溃,能不能维持正常运行. 二. Money是什么? Monkey测试是Android平台自动化测试的一种手段,通过Monkey程序模 ...
- Android自动化测试之Monkey命令使用及monkey脚本编写
系列文章 Android自动化测试环境部署及adb sdkmanager avdmanager Monitor DDMS工具使用及命令详解 Android自动化测试之Monkey使用及monkey脚本 ...
- android 获取monkey日志_Android压力测试:monkey压力测试实战
主要分享的主题是 Android App 专项测试, 通过 monkey 进行压力测试. 一.测试步骤 1.安装ADB 2.连接被测手机和电脑 3.打开CMD命令行 4.输入monkey命令 adb ...
- android 获取monkey日志_安卓app测试之Monkey日志分析
转:原文:https://blog.csdn.net/a136332462/article/details/76066909 一.一般测试结果分析-搜索关键字: 1.无响应问题可以在日志中搜索 &qu ...
最新文章
- 联机日志损坏时的恢复(非正常关闭数据库)
- URL跟Url的区别
- php-fpm启动后没有监听端口9000
- SAP Spartacus读取User Address的请求发送和接收源头
- 复制文本框内容至剪贴板
- JavaScript级联国家地区
- js获取DIV的位置坐标的三种方法!
- 【剑指Offer】18树的子结构
- Openstack Python 源代码的路径
- 数百种编程语言,而我为什么要学 Python?
- matlab-罗曼诺夫斯基准则剔除粗大值
- 正态分布下含绝对值的期望求解 -- 待验证
- Kubernetes 小白学习笔记(10)--搭建一个kubernetes集群-组建节点网络
- linux查看创建目录命令,Linux菜鸟——常见命令一 查看及创建目录文件等命令
- PHP 5 echo 和 print 语句
- MTK 刷机工具操作说明(多路)
- 计算机蓝屏代码0xc0000020,电脑运行程序时出现“损坏的映像错误0xc0000020”提示怎么办?...
- 计算机word打开,电脑word打不开怎么办
- 博客做外链不收录怎么办,如何利用博客做外链
- python生成扑克牌并实现比较大小玩法
热门文章
- nTrun(快速启动软件) V2.0.1 简体中文绿色版是什么
- 顶加载天线(圆盘)的设计与仿真
- 第二十二篇 射集跟随器偏置
- python画二维温度云图_“绘图,让化学生动起来”:Python-matplotlib绘图(全二维气相色谱图分析专题)...
- 数据分享|Spss Modeler关联规则Apriori模型、Carma算法分析超市顾客购买商品数据挖掘实例...
- 数据分析是什么?数据分析的现状是什么?
- ios 自动缩小字体_CSS:禁用iPhone上字体大小的自动缩放
- 很短很感人的爱情故事:经典伤感QQ日志
- 动易 用户控件 采集
- Python生成随机微软邮箱和密码