在【Android Monkey源码解析一】-系统执行 中,分析了Android系统中的Monkey命令的系统执行逻辑,通过app_process启动了Monkey的模块实现monkey.jar,入口函数为com.android.commands.monkey.Monkey,今天我们就先分析下该类的入口函数。
Monkey的实现是基于Java的,Java的程序主入口都是通过main方法来实现的,我们看下com.android.commands.monkey.Monkey类中的main方法定义,源码:https://android.googlesource.com/platform/development/+/refs/heads/android12-release/cmds/monkey/src/com/android/commands/monkey/Monkey.java#557 :
1 2 3 4 5 6 7 8 9 10 11 12 13 public static void main (String[] args) { Process.setArgV0("com.android.commands.monkey" ); Logger.err.println("args: " + Arrays.toString(args)); int resultCode = (new Monkey ()).run(args); System.exit(resultCode); }
main方法做了三件事情:
首先,设置启动的Monkey程序进程名称为:com.android.commands.monkey,可以在ps或者top命令中通过此名称来查看Monkey进程的相关信息。
其次,打印出所有的参数,并新建一个Monkey实例,调用其run方法,此方法返回一个结果码;
最后,通过第二步返回的结果码传给System的静态方法exit,结束程序。这里,二次开发Monkey的程序,可以通过命令行启动Monkey执行等待测试完成后,根据该返回值判断Monkey执行是否有异常(非0即存在异常)。
下面,重点来看下Monkey类的run方法,源码地址:https://android.googlesource.com/platform/development/+/refs/heads/android12-release/cmds/monkey/src/com/android/commands/monkey/Monkey.java#572 。
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 private int run (String[] args) { for (String s : args) { if ("--wait-dbg" .equals(s)) { Debug.waitForDebugger(); } } mVerbose = 0 ; mCount = 1000 ; mSeed = 0 ; mThrottle = 0 ; mArgs = args; for (String a: args) { Logger.err.println(" arg: \"" + a + "\"" ); } mNextArg = 0 ; for (int i = 0 ; i < MonkeySourceRandom.FACTORZ_COUNT; i++) { mFactors[i] = 1.0f ; } if (!processOptions()) { return -1 ; } if (!loadPackageLists()) { return -1 ; }
下面,分别介绍processOptions和loadPackageLists的流程。
processOptions:
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 private boolean processOptions () { if (mArgs.length < 1 ) { showUsage(); return false ; } try { String opt; Set<String> validPackages = new HashSet <>(); while ((opt = nextOption()) != null ) { if (opt.equals("-s" )) { mSeed = nextOptionLong("Seed" ); } else if (opt.equals("-p" )) { validPackages.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("--match-description" )) { mMatchDescription = nextOptionData(); } 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("--pct-permission" )) { int i = MonkeySourceRandom.FACTOR_PERMISSION; mFactors[i] = -nextOptionLong("runtime permission toggle 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" )) { } 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("--permission-target-system" )){ mPermissionTargetSystem = true ; } else if (opt.equals("-h" )) { showUsage(); return false ; } else { Logger.err.println("** Error: Unknown option: " + opt); showUsage(); return false ; } } MonkeyUtils.getPackageFilter().addValidPackages(validPackages); } catch (RuntimeException ex) { Logger.err.println("** Error: " + ex.toString()); showUsage(); return false ; } if (mServerPort == -1 ) { String countStr = nextArg(); if (countStr == null ) { Logger.err.println("** Error: Count not specified" ); showUsage(); return false ; } try { mCount = Integer.parseInt(countStr); } catch (NumberFormatException e) { Logger.err.println("** Error: Count is not a number: \"" + countStr + "\"" ); showUsage(); return false ; } } return true ; }
nextOption:
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 private String nextOption () { if (mNextArg >= mArgs.length) { return null ; } String arg = mArgs[mNextArg]; if (!arg.startsWith("-" )) { return null ; } mNextArg++; if (arg.equals("--" )) { return null ; } if (arg.length() > 1 && arg.charAt(1 ) != '-' ) { if (arg.length() > 2 ) { mCurArgData = arg.substring(2 ); return arg.substring(0 , 2 ); } else { mCurArgData = null ; return arg; } } mCurArgData = null ; Logger.err.println("arg=\"" + arg + "\" mCurArgData=\"" + mCurArgData + "\" mNextArg=" + mNextArg + " argwas=\"" + mArgs[mNextArg-1 ] + "\"" + " nextarg=\"" + mArgs[mNextArg] + "\"" ); return arg; } private String nextOptionData () { if (mCurArgData != null ) { return mCurArgData; } if (mNextArg >= mArgs.length) { return null ; } String data = mArgs[mNextArg]; Logger.err.println("data=\"" + data + "\"" ); mNextArg++; return data; }
loadPackageLists:
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 private boolean loadPackageLists () { if (((mPkgWhitelistFile != null ) || (MonkeyUtils.getPackageFilter().hasValidPackages())) && (mPkgBlacklistFile != null )) { Logger.err.println("** Error: you can not specify a package blacklist " + "together with a whitelist or individual packages (via -p)." ); return false ; } Set<String> validPackages = new HashSet <>(); if ((mPkgWhitelistFile != null ) && (!loadPackageListFromFile(mPkgWhitelistFile, validPackages))) { return false ; } MonkeyUtils.getPackageFilter().addValidPackages(validPackages); Set<String> invalidPackages = new HashSet <>(); if ((mPkgBlacklistFile != null ) && (!loadPackageListFromFile(mPkgBlacklistFile, invalidPackages))) { return false ; } MonkeyUtils.getPackageFilter().addInvalidPackages(invalidPackages); return true ; } private static boolean loadPackageListFromFile (String fileName, Set<String> list) { BufferedReader reader = null ; try { reader = new BufferedReader (new FileReader (fileName)); String s; while ((s = reader.readLine()) != null ) { s = s.trim(); if ((s.length() > 0 ) && (!s.startsWith("#" ))) { list.add(s); } } } catch (IOException ioe) { Logger.err.println("" + ioe); return false ; } finally { if (reader != null ) { try { reader.close(); } catch (IOException ioe) { Logger.err.println("" + ioe); } } } return true ; }
接下来,我们先看下MonkeyUtils的解析实现。
本文链接: http://longzhiye.top/2025/10/05/2025-10-05/