Monkey学习笔记
程序员文章站
2022-05-11 12:21:37
...
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