1、运行环境
base=/system export CLASSPATH=$base/framework/monkey.jar trap "" HUP exec app_process $base/bin com.android.commands.monkey.Monkey $*
monkey是一个shell脚本,在目录“/system/bin/monkey”下,通过app_process执行monkey.jar
2、程序入口
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方法,里面主要干了三件事
- 初始化参数
- 初始化事件源
- 执行事件
3、初始化参数
调用processOptions方法初始化
private boolean processOptions() { // quick (throwaway) check for unadorned command if (mArgs.length < 1) { showUsage(); return false; } try { String opt; while ((opt = nextOption()) != null) { if (opt.equals("-s")) { mSeed = nextOptionLong("Seed"); } else if (opt.equals("-p")) { mValidPackages.add(nextOptionData()); } else if (opt.equals("-c")) { mMainCategories.add(nextOptionData()); } else if (opt.equals("-v")) { mVerbose += 1; } else if (opt.equals("--ignore-crashes")) { mIgnoreCrashes = true; } else if (opt.equals("--ignore-timeouts")) { mIgnoreTimeouts = true; } else if (opt.equals("--ignore-security-exceptions")) { mIgnoreSecurityExceptions = true; } else if (opt.equals("--monitor-native-crashes")) { mMonitorNativeCrashes = true; } else if (opt.equals("--ignore-native-crashes")) { mIgnoreNativeCrashes = true; } else if (opt.equals("--kill-process-after-error")) { mKillProcessAfterError = true; } else if (opt.equals("--hprof")) { mGenerateHprof = true; } else if (opt.equals("--pct-touch")) { int i = MonkeySourceRandom.FACTOR_TOUCH; mFactors[i] = -nextOptionLong("touch events percentage"); } else if (opt.equals("--pct-motion")) { int i = MonkeySourceRandom.FACTOR_MOTION; mFactors[i] = -nextOptionLong("motion events percentage"); } else if (opt.equals("--pct-trackball")) { int i = MonkeySourceRandom.FACTOR_TRACKBALL; mFactors[i] = -nextOptionLong("trackball events percentage"); } else if (opt.equals("--pct-rotation")) { int i = MonkeySourceRandom.FACTOR_ROTATION; mFactors[i] = -nextOptionLong("screen rotation events percentage"); } else if (opt.equals("--pct-syskeys")) { int i = MonkeySourceRandom.FACTOR_SYSOPS; mFactors[i] = -nextOptionLong("system (key) operations percentage"); } else if (opt.equals("--pct-nav")) { int i = MonkeySourceRandom.FACTOR_NAV; mFactors[i] = -nextOptionLong("nav events percentage"); } else if (opt.equals("--pct-majornav")) { int i = MonkeySourceRandom.FACTOR_MAJORNAV; mFactors[i] = -nextOptionLong("major nav events percentage"); } else if (opt.equals("--pct-appswitch")) { int i = MonkeySourceRandom.FACTOR_APPSWITCH; mFactors[i] = -nextOptionLong("app switch events percentage"); } else if (opt.equals("--pct-flip")) { int i = MonkeySourceRandom.FACTOR_FLIP; mFactors[i] = -nextOptionLong("keyboard flip percentage"); } else if (opt.equals("--pct-anyevent")) { int i = MonkeySourceRandom.FACTOR_ANYTHING; mFactors[i] = -nextOptionLong("any events percentage"); } else if (opt.equals("--pct-pinchzoom")) { int i = MonkeySourceRandom.FACTOR_PINCHZOOM; mFactors[i] = -nextOptionLong("pinch zoom events percentage"); } else if (opt.equals("--pkg-blacklist-file")) { mPkgBlacklistFile = nextOptionData(); } else if (opt.equals("--pkg-whitelist-file")) { mPkgWhitelistFile = nextOptionData(); } else if (opt.equals("--throttle")) { mThrottle = nextOptionLong("delay (in milliseconds) to wait between events"); } else if (opt.equals("--randomize-throttle")) { mRandomizeThrottle = true; } else if (opt.equals("--wait-dbg")) { // do nothing - it's caught at the very start of run() } else if (opt.equals("--dbg-no-events")) { mSendNoEvents = true; } else if (opt.equals("--port")) { mServerPort = (int) nextOptionLong("Server port to listen on for commands"); } else if (opt.equals("--setup")) { mSetupFileName = nextOptionData(); } else if (opt.equals("-f")) { mScriptFileNames.add(nextOptionData()); } else if (opt.equals("--profile-wait")) { mProfileWaitTime = nextOptionLong("Profile delay" + " (in milliseconds) to wait between user action"); } else if (opt.equals("--device-sleep-time")) { mDeviceSleepTime = nextOptionLong("Device sleep time" + "(in milliseconds)"); } else if (opt.equals("--randomize-script")) { mRandomizeScript = true; } else if (opt.equals("--script-log")) { mScriptLog = true; } else if (opt.equals("--bugreport")) { mRequestBugreport = true; } else if (opt.equals("--periodic-bugreport")){ mGetPeriodicBugreport = true; mBugreportFrequency = nextOptionLong("Number of iterations"); } else if (opt.equals("-h")) { showUsage(); return false; } else { System.err.println("** Error: Unknown option: " + opt); showUsage(); return false; } } } catch (RuntimeException ex) { System.err.println("** Error: " + ex.toString()); showUsage(); return false; } // If a server port hasn't been specified, we need to specify // a count if (mServerPort == -1) { String countStr = nextArg(); if (countStr == null) { System.err.println("** Error: Count not specified"); showUsage(); return false; } try { mCount = Integer.parseInt(countStr); } catch (NumberFormatException e) { System.err.println("** Error: Count is not a number"); showUsage(); return false; } } return true; }
根据命令行参数初始化对应各个参数
4、初始化系统接口
调用getSystemInterfaces初始化系统接口
private boolean getSystemInterfaces() { mAm = ActivityManagerNative.getDefault(); if (mAm == null) { System.err.println("** Error: Unable to connect to activity manager; is the system " + "running?"); return false; } mWm = IWindowManager.Stub.asInterface(ServiceManager.getService("window")); if (mWm == null) { System.err.println("** Error: Unable to connect to window manager; is the system " + "running?"); return false; } mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package")); if (mPm == null) { System.err.println("** Error: Unable to connect to package manager; is the system " + "running?"); return false; } try { mAm.setActivityController(new ActivityController()); mNetworkMonitor.register(mAm); } catch (RemoteException e) { System.err.println("** Failed talking with activity manager!"); return false; } return true; }
主要初始化IActivityManager、IWindowManager、IPackageManager三个接口,还有添加监控和注册广播
mAm.setActivityController(new ActivityController());
private class ActivityController extends IActivityController.Stub { public boolean activityStarting(Intent intent, String pkg) { boolean allow = checkEnteringPackage(pkg) || (DEBUG_ALLOW_ANY_STARTS != 0); if (mVerbose > 0) { // StrictMode's disk checks end up catching this on // userdebug/eng builds due to PrintStream going to a // FileOutputStream in the end (perhaps only when // redirected to a file?) So we allow disk writes // around this region for the monkey to minimize // harmless dropbox uploads from monkeys. StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites(); System.out.println(" // " + (allow ? "Allowing" : "Rejecting") + " start of " + intent + " in package " + pkg); StrictMode.setThreadPolicy(savedPolicy); } currentPackage = pkg; currentIntent = intent; return allow; } public boolean activityResuming(String pkg) { StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites(); System.out.println(" // activityResuming(" + pkg + ")"); boolean allow = checkEnteringPackage(pkg) || (DEBUG_ALLOW_ANY_RESTARTS != 0); if (!allow) { if (mVerbose > 0) { System.out.println(" // " + (allow ? "Allowing" : "Rejecting") + " resume of package " + pkg); } } currentPackage = pkg; StrictMode.setThreadPolicy(savedPolicy); return allow; } public boolean appCrashed(String processName, int pid, String shortMsg, String longMsg, long timeMillis, String stackTrace) { StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites(); System.err.println("// CRASH: " + processName + " (pid " + pid + ")"); System.err.println("// Short Msg: " + shortMsg); System.err.println("// Long Msg: " + longMsg); System.err.println("// Build Label: " + Build.FINGERPRINT); System.err.println("// Build Changelist: " + Build.VERSION.INCREMENTAL); System.err.println("// Build Time: " + Build.TIME); System.err.println("// " + stackTrace.replace("\n", "\n// ")); StrictMode.setThreadPolicy(savedPolicy); if (!mIgnoreCrashes || mRequestBugreport) { synchronized (Monkey.this) { if (!mIgnoreCrashes) { mAbort = true; } if (mRequestBugreport){ mRequestAppCrashBugreport = true; mReportProcessName = processName; } } return !mKillProcessAfterError; } return false; } public int appEarlyNotResponding(String processName, int pid, String annotation) { return 0; } public int appNotResponding(String processName, int pid, String processStats) { StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites(); System.err.println("// NOT RESPONDING: " + processName + " (pid " + pid + ")"); System.err.println(processStats); StrictMode.setThreadPolicy(savedPolicy); synchronized (Monkey.this) { mRequestAnrTraces = true; mRequestDumpsysMemInfo = true; mRequestProcRank = true; if (mRequestBugreport){ mRequestAnrBugreport = true; mReportProcessName = processName; } } if (!mIgnoreTimeouts) { synchronized (Monkey.this) { mAbort = true; } } return (mKillProcessAfterError) ? -1 : 1; } }
public void register(IActivityManager am) throws RemoteException { if (LDEBUG) System.out.println("registering Receiver"); am.registerReceiver(null, null, this, filter, null, UserHandle.USER_ALL); }
public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) throws RemoteException { NetworkInfo ni = (NetworkInfo) intent.getParcelableExtra( ConnectivityManager.EXTRA_NETWORK_INFO); if (LDEBUG) System.out.println("Network state changed: " + "type=" + ni.getType() + ", state=" + ni.getState()); updateNetworkStats(); if (NetworkInfo.State.CONNECTED == ni.getState()) { if (LDEBUG) System.out.println("Network connected"); mLastNetworkType = ni.getType(); } else if (NetworkInfo.State.DISCONNECTED == ni.getState()) { if (LDEBUG) System.out.println("Network not connected"); mLastNetworkType = -1; // unknown since we're disconnected } mEventTime = SystemClock.elapsedRealtime(); }
- 添加对activity的监控
- 添加对网络的监控
5、初始化事件源
if (mScriptFileNames != null && mScriptFileNames.size() == 1) { // script mode, ignore other options mEventSource = new MonkeySourceScript(mRandom, mScriptFileNames.get(0), mThrottle, mRandomizeThrottle, mProfileWaitTime, mDeviceSleepTime); mEventSource.setVerbose(mVerbose); mCountEvents = false; } else if (mScriptFileNames != null && mScriptFileNames.size() > 1) { if (mSetupFileName != null) { mEventSource = new MonkeySourceRandomScript(mSetupFileName, mScriptFileNames, mThrottle, mRandomizeThrottle, mRandom, mProfileWaitTime, mDeviceSleepTime, mRandomizeScript); mCount++; } else { mEventSource = new MonkeySourceRandomScript(mScriptFileNames, mThrottle, mRandomizeThrottle, mRandom, mProfileWaitTime, mDeviceSleepTime, mRandomizeScript); } mEventSource.setVerbose(mVerbose); mCountEvents = false; } else if (mServerPort != -1) { try { mEventSource = new MonkeySourceNetwork(mServerPort); } catch (IOException e) { System.out.println("Error binding to network socket."); return -5; } mCount = Integer.MAX_VALUE; } else { // random source by default if (mVerbose >= 2) { // check seeding performance System.out.println("// Seeded: " + mSeed); } mEventSource = new MonkeySourceRandom(mRandom, mMainApps, mThrottle, mRandomizeThrottle); mEventSource.setVerbose(mVerbose); // set any of the factors that has been set for (int i = 0; i < MonkeySourceRandom.FACTORZ_COUNT; i++) { if (mFactors[i] <= 0.0f) { ((MonkeySourceRandom) mEventSource).setFactors(i, mFactors[i]); } } // in random mode, we start with a random activity ((MonkeySourceRandom) mEventSource).generateActivity(); }
事件源代表测试数据的事件是从哪里过来的,不同的event source会有不同的类来做相应的实现:
- MonkeySourceNetwork.java: 事件是从网络如monkeyrunner过来的,处理的是界面控制操作事件
- MonkeySourceNetworkVars.java: 事件也是从网络如monkeyrunner过来的,处理的是getPropery事件
- MonkeySourceNetworkViews.java:事件也是从网络如monkeyrunner过来的,处理的是Views相关的事件
- MonkeySourceRandom.java:事件是从monkey内部生成的随机事件集,也就是我们通过命令行启动monkey测试目标app的常用方式
- MonkeySourceRanodomeScript.java: 上面的随机内部数据源也可以通过指定setup脚本来创建
- MonkeySourceScript.java: 用户也可以遵循一定的规则编写monkey脚本来驱动monkey进行相关测试,与上面不同的是它不再是随机的
6、执行事件
调用runMonkeyCycles方法循环执行事件
把命令翻译成monkey事件然后放到命令队列EventQueue
以monkey自身随机事件源为例
... ((MonkeySourceRandom) mEventSource).generateActivity();
public void generateActivity() { MonkeyActivityEvent e = new MonkeyActivityEvent(mMainApps.get( mRandom.nextInt(mMainApps.size()))); mQ.addLast(e); }
首先在初始化事件源时,会初始化一个MonkeyActivityEvent事件添加到EventQueue
MonkeyEvent ev = mEventSource.getNextEvent();
public MonkeyEvent getNextEvent() { if (mQ.isEmpty()) { generateEvents(); } mEventCount++; MonkeyEvent e = mQ.getFirst(); mQ.removeFirst(); return e; }
private void generateEvents() { float cls = mRandom.nextFloat(); int lastKey = 0; if (cls < mFactors[FACTOR_TOUCH]) { generatePointerEvent(mRandom, GESTURE_TAP); return; } else if (cls < mFactors[FACTOR_MOTION]) { generatePointerEvent(mRandom, GESTURE_DRAG); return; } else if (cls < mFactors[FACTOR_PINCHZOOM]) { generatePointerEvent(mRandom, GESTURE_PINCH_OR_ZOOM); return; } else if (cls < mFactors[FACTOR_TRACKBALL]) { generateTrackballEvent(mRandom); return; } else if (cls < mFactors[FACTOR_ROTATION]) { generateRotationEvent(mRandom); return; } // The remaining event categories are injected as key events for (;;) { if (cls < mFactors[FACTOR_NAV]) { lastKey = NAV_KEYS[mRandom.nextInt(NAV_KEYS.length)]; } else if (cls < mFactors[FACTOR_MAJORNAV]) { lastKey = MAJOR_NAV_KEYS[mRandom.nextInt(MAJOR_NAV_KEYS.length)]; } else if (cls < mFactors[FACTOR_SYSOPS]) { lastKey = SYS_KEYS[mRandom.nextInt(SYS_KEYS.length)]; } else if (cls < mFactors[FACTOR_APPSWITCH]) { MonkeyActivityEvent e = new MonkeyActivityEvent(mMainApps.get( mRandom.nextInt(mMainApps.size()))); mQ.addLast(e); return; } else if (cls < mFactors[FACTOR_FLIP]) { MonkeyFlipEvent e = new MonkeyFlipEvent(mKeyboardOpen); mKeyboardOpen = !mKeyboardOpen; mQ.addLast(e); return; } else { lastKey = 1 + mRandom.nextInt(KeyEvent.getMaxKeyCode() - 1); } if (lastKey != KeyEvent.KEYCODE_POWER && lastKey != KeyEvent.KEYCODE_ENDCALL && PHYSICAL_KEY_EXISTS[lastKey]) { break; } } MonkeyKeyEvent e = new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, lastKey); mQ.addLast(e); e = new MonkeyKeyEvent(KeyEvent.ACTION_UP, lastKey); mQ.addLast(e); }
循环到EventQueue获取事件并执行,如果为空的话,就随机生成事件并添加到EventQueue中
事件执行完毕就从EventQueue中删除
3、执行事件通过事件注入方式执行,MonkeyEvent实现类都实现了父类的injectEvent方法
这次学习借鉴:http://blog.csdn.net/zhubaitian/article/details/40395327
相关推荐
### Android测试工具Monkey学习笔记 #### 一、Monkey工具简介 Monkey是Android系统自带的一款强大的自动化测试工具,常被用于产品的稳定性测试。该工具能够模拟各种用户行为,如按键输入、触摸屏幕、手势操作等,...
Java相关课程系列笔记之一Java学习笔记 Java相关课程系列笔记之四JDBC学习笔记 Java相关课程系列笔记之六HTML学习笔记 Java相关课程系列笔记之七CSS学习笔记 Java相关课程系列笔记之八JavaScript学习笔记 Java相关...
### Velocity学习笔记精要 **一、Velocity简介与特点** Velocity是一种基于Java的模板引擎,用于将静态数据和动态内容结合在一起,生成最终的HTML、XML或其他格式的文档。其最大的特点是性能高、易于理解和使用,...
mac monkey搭建及使用,来自于一个测试新人的学习笔记,有不正确的地方还请指教
10. **教育应用**:在教育领域,Tampermonkey可用于创建脚本来辅助在线学习,比如自动播放英语听力、自动整理学习笔记等,为学习过程带来便利。 总之,Tampermonkey是互联网时代提高效率、个性化浏览的强大工具,...
monkey是安卓系统自带的,其启动脚本位于安卓系统的system/bin目录下的monkey文件 monkey是一个程序,运行在模拟器或设备上,并生成伪随机的用户事件流。如点击、触摸、手势,以及一些系统级事件,可以使用monkey...
Android学习笔记全全整理,是针对想要深入理解并掌握Android开发技术的学习者们的一份宝贵资源。这份笔记涵盖了从基础到高级的多个方面,旨在帮助读者建立起完整的Android知识体系。以下将详细介绍其中可能包含的...
综上所述,这一系列学习笔记涵盖了并发编程的关键概念和实战技巧,包括Java内存模型、线程池、并发容器的使用以及常见数据结构的线程安全问题。通过深入学习这些内容,开发者可以更好地理解和解决多线程环境下的编程...
JavaScript权威指南的学习笔记为我们深入理解这门语言的基础和特性提供了宝贵的资源。在第一部分中,主要探讨了JavaScript的核心概念和基本语法。 首先,JavaScript有两个主要的解释器:C语言编写的SpiderMonkey和...
11. [Monkeyrunner初学笔记](无链接) - 记录了学习Monkeyrunner过程中的心得体会和实践案例。 12. [Monkeyrunner全方位解析](无链接) - 包含了Monkeyrunner的命令行、文件以及Eclipse平台的使用方法。 了解和掌握...
这篇"prototype学习笔记"可能探讨了如何利用原型链实现面向对象编程的关键技术。以下是对这个主题的详细解析。 首先,理解`prototype`的基本含义是关键。在JavaScript中,每个函数都有一个`prototype`属性,这个...
【Android 学习笔记】 Android 是一款开源的操作系统,主要用于移动设备,如智能手机和平板电脑。这个学习笔记包含了从基础到进阶的各种Android开发知识点,旨在帮助开发者更好地理解和掌握Android应用开发。 1. *...
### Oracle学习笔记知识点详解 #### 一、SPPOOL命令 - **定义**: SPPOOL命令用于将屏幕输出重定向到一个文件中。这对于保存查询结果、脚本执行过程等非常有用。 - **语法**: - `spool 文件路径`: 开始将后续的输出...
学习笔记代码 1,目前完成了全部代码的更新 2,前五章代码为本人手码,第六章开始为二进制文件,转换,前五章中的章节文件夹中的代码为原始码,供大家参考 3,第五章中的代码部分API为TensorFlow1.0版本中的代码,请...
:see-no-evil_monkey: :frog: :panda: :horse_face: :spouting_whale: 程序猿学习资料集邮(Program ape learning materials) 编程学习中经常要访问一些网站找资料,为了以后便于查找,在这里做下整理。...