Android Monkey(转载)
这两天在读Android Monkey的源代码.代码不多,放出分享.
我现说一下,Monkey是干什么的:简单的说就是,模拟用户的touch screen和keyboard的输入.其实这个功能就已经很恐怖了. Google自己说的下面:
// Only bind this to local host. This means that you can only
// talk to the monkey locally, or though adb port forwarding.
就是觊觎这个强大的力量.Monkey是用JAVA写成的,但是我们确可以这样运行:
$adb shell monkey ......
这是为什么呢?是因为在/system/bin目录下有一个monkey的shell脚本.内容如下:
# Script to start "monkey" on the device, which has a very rudimentary
# shell.
#
base=/system
export CLASSPATH=$base/framework/monkey.jar
exec app_process $base/bin com.android.commands.monkey.Monkey $*
exec 会运行起/system/framework/monkey.jar这只Java程序.后面我们还会用到这个脚本.
先给出Monkey的架构图:
又是我的手绘图.哈哈.实际上,并不复杂.
Monkey.java 里面有main()函数.而main()->Run().Run()里面作了下面的操作:
1.processOptions()初始化参数(通过传入入参来初始化参数).还有一些的操作函数如:nextOption() nextOptionLong()。。
2.做接口的检查,也就是申请这些接口资源.
3.关键的一步:
if(mServerPort!=1)
{ try{
mEventSource = new MonkeySourceNetWork(mServerPort);
...省略
开始ServerSocket.监听用户输入的command.
4.mNetworkMonitor.Start();开始监控网络状态
5.关键的一步:
runMonkeyCycles(),代码如下:
while()
{
MonkeyEvent ev = mEventSource.getNextEvent();
ev.inject(....);
...省略
开始Event loop,把每个CommandQueue中的消息都执行.
6.mNetworkMonitor.Stop()
这里我们看到使用了SocketServer,所以理论上来说,可以在PC端发送command给device,但是必须透过USB的连接.这点我在上面已经说的很清楚了.(PC端,Android不可能帮你实现,我们只要在PC端使用Socket向device发送命令就可以了).
好了,我们接着往下分析:
MonkeyEvent的实现是典型的OO思想.
MonkeyEvent.java仅仅是一个abstract class.
最重要的方法都由子类去实现.(把Event放入当前的Activity去运行)
public abstract int injectEvent(IWindowManager iwm, IActivityManager iam, int verbose);
monkey*Event.java都是MonkeyEvent.java的子类.
来讲一下,MonkeySourceNetwork.java.
这个类相对较大,里面有许多的内嵌类.(ps. 我不是写Java出身的,看到内嵌类就不符合我的审美习惯)
1.有一个MonkeyCommand的类,这是一个interface. *Command都是他的子类.
其中定义了一个很重要的方法:这个方法将string 类型的command转换为MonkeyEvent对象,并放入
commandQueue.
MonkeyCommandReturn translateCommand(List<String> command, CommandQueue queue);
好了,首先MonkeyCommandReturn又是一个类,是命令执行成功还是失败的一个类.
commandQueue是实际上是一个interface,然后它的实现是在CommandQueueImpl这个类中.
实现了如下的方法:
public void enqueueEvent(MonkeyEvent e) {
queuedEvents.offer(e);
}
将Event放入queueEvents队列中.而queueEvents是这样定义的:
private final Queue<MonkeyEvent> queuedEvents = new LinkedList<MonkeyEvent>();
无非就是一个LinkedList而已拉.
2.下面我在来说说MonkeySourceNetwork类中的方法:
private void translateCommand(String commandLine)
和上面的方法同名.实际上这个translateCommand是上面的封装.我们来RTFS:
List<String> parts = commandLineSplit(commandLine);
if (parts.size() > 0) {
MonkeyCommand command = COMMAND_MAP.get(parts.get(0));
if (command != null) {
MonkeyCommandReturn ret = command.translateCommand(parts,commandQueue);
COMMAND_MAP这是个什么东西呢?
private static final Map<String, MonkeyCommand> COMMAND_MAP = new HashMap<String, MonkeyCommand>();
看了没,是一个Map.以Command string作为Key, MonkeyCommand作为Value.这样的话,就建立了,
输入的命令和Command的关系。
static {
// Add in all the commands we support
COMMAND_MAP.put("flip", new FlipCommand());
COMMAND_MAP.put("touch", new TouchCommand());
COMMAND_MAP.put("trackball", new TrackballCommand());
COMMAND_MAP.put("key", new KeyCommand());
COMMAND_MAP.put("sleep", new SleepCommand());
COMMAND_MAP.put("wake", new WakeCommand());
COMMAND_MAP.put("tap", new TapCommand());
COMMAND_MAP.put("press", new PressCommand());
COMMAND_MAP.put("type", new TypeCommand());
COMMAND_MAP.put("listvar", new MonkeySourceNetworkVars.ListVarCommand());
COMMAND_MAP.put("getvar", new MonkeySourceNetworkVars.GetVarCommand());
}
但也不是所有的命令都放入COMMAND_MAP中.quit 和 done 就是例外,原因也很简单,没必要放进Map.
再来分析下这条语句:MonkeyCommandReturn ret = command.translateCommand(parts,commandQueue);
首先,command已经是MonkeyCommand的sub class了.so 调用translateCommand将会表现出不同的行为.
例如,command假设为PressCommand.则translateCommand的表现为:
queue.enqueueEvent(new MonkeyKeyEvent(....))
每一个MonkeyEvent又都代表了一些对AP GUI错作.所以,也就是把操作放入了Queue.
okay.就这么多了.才不多这次就说这么多吧.我再附上我的手绘图,希望有帮助.