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.就这么多了.才不多这次就说这么多吧.我再附上我的手绘图,希望有帮助.

   

posted @ 2012-04-15 22:49  博客园的阿拉丁  阅读(3003)  评论(0编辑  收藏  举报