这里简单说一下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相关推荐

  1. android crash没有日志_App测试之monkey(四)-调试参数及日志

    由于monkey在测试app时,我们需要作长时间的稳定性测试,比如连续测试10小时(monkey不能指定时间,可以指定次数,时间可以在测试次数的日志基础上大概算出来),在测试过程中,app很可能测试时 ...

  2. android monkey原理_Android Monkey原理探讨-阿里云开发者社区

    0x0 概述 本文不涉及如何使用monkey,官网说得够详细了,网上资料也一大堆.本文着重探讨monkey的实现原理,以及基于这些原理,我们可以做些什么?本文涉及的Monkey的源码位于AOSP的de ...

  3. monkey测试_安卓测试之monkey

    安卓手机最简单的随机压力测试工具monkey真的是测试的福星,人人都可以用,只需要敲一串命令即可: 举例:adb shell monkey -p com.android.camera --ignore ...

  4. Android Studio 忽略_Android性能优化--启动优化

    1. 前言 一个应用App的启动速度能够影响用户的首次体验,启动速度较慢(感官上)的应用可能导致用户再次开启App的意图下降,或者卸载放弃该应用程序.本文会通过以下几个方面来介绍应用启动的相关指标和优 ...

  5. Monkey原理初步和改良优化--Android自动化测试学习历程

    章节:自动化基础篇--Monkey原理初步和改良优化(第三讲) 主要讲解内容与笔记: 一.理论知识: 直接看文档,来了解monkey的概念.基本原理,以及如何使用. First,what is And ...

  6. [Android 测试] 压力稳定性测试之: Monkey 详解分析脚本

    一.什么是稳定性测试? 通过随机点击屏幕一段时间,看看app会不会奔溃,能不能维持正常运行. 二. Money是什么? Monkey测试是Android平台自动化测试的一种手段,通过Monkey程序模 ...

  7. Android自动化测试之Monkey命令使用及monkey脚本编写

    系列文章 Android自动化测试环境部署及adb sdkmanager avdmanager Monitor DDMS工具使用及命令详解 Android自动化测试之Monkey使用及monkey脚本 ...

  8. android 获取monkey日志_Android压力测试:monkey压力测试实战

    主要分享的主题是 Android App 专项测试, 通过 monkey 进行压力测试. 一.测试步骤 1.安装ADB 2.连接被测手机和电脑 3.打开CMD命令行 4.输入monkey命令 adb ...

  9. android 获取monkey日志_安卓app测试之Monkey日志分析

    转:原文:https://blog.csdn.net/a136332462/article/details/76066909 一.一般测试结果分析-搜索关键字: 1.无响应问题可以在日志中搜索 &qu ...

最新文章

  1. 联机日志损坏时的恢复(非正常关闭数据库)
  2. URL跟Url的区别
  3. php-fpm启动后没有监听端口9000
  4. SAP Spartacus读取User Address的请求发送和接收源头
  5. 复制文本框内容至剪贴板
  6. JavaScript级联国家地区
  7. js获取DIV的位置坐标的三种方法!
  8. 【剑指Offer】18树的子结构
  9. Openstack Python 源代码的路径
  10. 数百种编程语言,而我为什么要学 Python?
  11. matlab-罗曼诺夫斯基准则剔除粗大值
  12. 正态分布下含绝对值的期望求解 -- 待验证
  13. Kubernetes 小白学习笔记(10)--搭建一个kubernetes集群-组建节点网络
  14. linux查看创建目录命令,Linux菜鸟——常见命令一 查看及创建目录文件等命令
  15. PHP 5 echo 和 print 语句
  16. MTK 刷机工具操作说明(多路)
  17. 计算机蓝屏代码0xc0000020,电脑运行程序时出现“损坏的映像错误0xc0000020”提示怎么办?...
  18. 计算机word打开,电脑word打不开怎么办
  19. 博客做外链不收录怎么办,如何利用博客做外链
  20. python生成扑克牌并实现比较大小玩法

热门文章

  1. nTrun(快速启动软件) V2.0.1 简体中文绿色版是什么
  2. 顶加载天线(圆盘)的设计与仿真
  3. 第二十二篇 射集跟随器偏置
  4. python画二维温度云图_“绘图,让化学生动起来”:Python-matplotlib绘图(全二维气相色谱图分析专题)...
  5. 数据分享|Spss Modeler关联规则Apriori模型、Carma算法分析超市顾客购买商品数据挖掘实例...
  6. 数据分析是什么?数据分析的现状是什么?
  7. ios 自动缩小字体_CSS:禁用iPhone上字体大小的自动缩放
  8. 很短很感人的爱情故事:经典伤感QQ日志
  9. 动易 用户控件 采集
  10. Python生成随机微软邮箱和密码