简介:此类用于生成本机系统输入事件,出于测试自动化、自运行演示和其他可以控制鼠标和键盘的应用程序
这是需要的。
机器人的主要目的是方便用户
Java平台实现的自动化测试
方法介绍及解析:以下private开头方法为Robot内部使用,方法,public方法为对外开放方法,请重点关注public方法
使用要点 |
代码示例 |
说明 |
1.实例化方法 | new Robot(); | 无参实例化,详见1 |
new Robot(
|
带参实例化,详见2 | |
2.屏幕截图 |
Robot robot = new Robot(); //初始化Robot对象 Rectangle rect = new Rectangle(0, 0, 50, 50); //构建范围 BufferedImage buffer = robot.createScreenCapture(rect); //截取 |
详见17,截取固定区域屏幕图片 |
3.延时休眠 | robot.delay(1000); | //延时1秒,详见23 |
4.键盘模拟 | robot.keyPress(KeyEvent.VK_A); | 按下A键,键盘上所有按键的ASCII码都在KeyEvent中定义了静态常量 |
robot.keyRelease(KeyEvent.VK_A); | 弹起A键 | |
5.鼠标模拟 | robot.mouseMove(10,20); | 将鼠标移动至(10,20)这个坐标点 |
robot.mousePress(InputEvent.BUTTON1_MASK);//左键 robot.mousePress(InputEvent.BUTTON2_MASK);//中键(早期鼠标才有,已被滚轮替代) robot.mousePress(InputEvent.BUTTON3_MASK)//右键 |
鼠标按下事件 | |
robot.mouseRelease(InputEvent.BUTTON1_MASK);//左键 robot.mouseRelease(InputEvent.BUTTON2_MASK);//中键(早期鼠标才有,已被滚轮替代) robot.mouseRelease(InputEvent.BUTTON3_MASK)//右键 |
鼠标弹起事件 | |
robot.mouseWheel(key); | 鼠标滚轮事件,详见12 |
1.Robot无参构造方法:此方法作用是在主屏幕构建机器人对象。GraphicsEnvironment.isHeadless()此方法是测试当前服务器鼠标键盘是否支持GraphicsEnvironment.getLocalGraphicsEnvironment() .getDefaultScreenDevice(),其中GraphicsEnvironment.getLocalGraphicsEnvironment()为获取当前系统环境,getDefaultScreenDevice为获取默认屏幕设备
init方法为将此屏幕初始化进Robot对象中,iinit()方法详解见下文第3点
public Robot() throws AWTException {
if (GraphicsEnvironment.isHeadless()) {
throw new AWTException("headless environment");
}
init(GraphicsEnvironment.getLocalGraphicsEnvironment()
.getDefaultScreenDevice());
}
2.Robot带参构造方法:此方法作用为给定屏幕创建机器人对象(注意此方法重点,实现钩子函数必须掌握,可在后台运行得点击脚本会依赖此方法)
checkIsScreenDevice:检查screen是否为屏幕设备
init方法为将此屏幕初始化进Robot对象中,init()方法详解见下文第3点
public Robot(GraphicsDevice screen) throws AWTException {
checkIsScreenDevice(screen);
init(screen);
}
3.init方法:初始化屏幕,将屏幕写入服务内存中
checkRobotAllowed:检查当前设备安全策略是否允许创建机器人
Toolkit toolkit = Toolkit.getDefaultToolkit();//获取默认工具箱,此方法加入synchronized标识,即为线程安全,判断Toolkit是否为ComponentFactory工具包
初始化RobotPeer
初始化机器人处理器RobotDisposer
sun.java2d.Disposer.addRecord(anchor, disposer);//将机器人处理器加入window系统处理器中,此代码含义是执行调用windows底层处理器
initLegalButtonMask:初始化鼠标按钮掩码,详见第4点
private void init(GraphicsDevice screen) throws AWTException {
checkRobotAllowed();
Toolkit toolkit = Toolkit.getDefaultToolkit();
if (toolkit instanceof ComponentFactory) {
peer = ((ComponentFactory)toolkit).createRobot(this, screen);
disposer = new RobotDisposer(peer);
sun.java2d.Disposer.addRecord(anchor, disposer);
}
initLegalButtonMask();
}
4.initLegalButtonMask:初始化合法的鼠标按钮掩码(所谓掩码即为ASCII码)。
- 判定按钮使用标识LEGAL_BUTTON_MASK,0为默认未使用,1为已使用
- 判定默认工具箱中是否允许处理和发布额外的鼠标按钮事件
- 获取鼠标按钮数
- 将鼠标按钮值做位或运算,并存入tmpMask中
private static synchronized void initLegalButtonMask() {
if (LEGAL_BUTTON_MASK != 0) return;
int tmpMask = 0;
if (Toolkit.getDefaultToolkit().areExtraMouseButtonsEnabled()){
if (Toolkit.getDefaultToolkit() instanceof SunToolkit) {
final int buttonsNumber = ((SunToolkit)(Toolkit.getDefaultToolkit())).getNumberOfButtons();
for (int i = 0; i < buttonsNumber; i++){
tmpMask |= InputEvent.getMaskForButton(i+1);
}
}
}
tmpMask |= InputEvent.BUTTON1_MASK|
InputEvent.BUTTON2_MASK|
InputEvent.BUTTON3_MASK|
InputEvent.BUTTON1_DOWN_MASK|
InputEvent.BUTTON2_DOWN_MASK|
InputEvent.BUTTON3_DOWN_MASK;
LEGAL_BUTTON_MASK = tmpMask;
}
5.checkRobotAllowed:判定当前设备的安全策略机制
- 获取当前系统安全管理器
- 检测安全管理器是否允许创建Robot机器人
private void checkRobotAllowed() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkPermission(SecurityConstants.AWT.CREATE_ROBOT_PERMISSION);
}
}
6.checkIsScreenDevice:检测屏幕设备。检测device是否是一个屏幕设备
private void checkIsScreenDevice(GraphicsDevice device) {
if (device == null || device.getType() != GraphicsDevice.TYPE_RASTER_SCREEN) {
throw new IllegalArgumentException("not a valid screen device");
}
}
7.RobotDisposer :机器人处理器类
static class RobotDisposer implements sun.java2d.DisposerRecord {
private final RobotPeer peer;
public RobotDisposer(RobotPeer peer) {
this.peer = peer;
}
public void dispose() {
if (peer != null) {
peer.dispose();
}
}
}
8.mouseMove:鼠标移动方法(线程安全),其中x,y为当前设备像素点
public synchronized void mouseMove(int x, int y) {
peer.mouseMove(x, y);
afterEvent();
}
9:mousePress:鼠标按下事件,传入的参数buttons,为鼠标哪一个键,其常用参数如下
InputEvent.BUTTON1_MASK:鼠标左键
InputEvent.BUTTON2_MASK:鼠标滚轮
InputEvent.BUTTON3_MASK:鼠标右键
public synchronized void mousePress(int buttons) {
checkButtonsArgument(buttons);
peer.mousePress(buttons);
afterEvent();
}
10:mouseRelease:鼠标弹起事件,传入的参数和mousePress类似,此方法一般和mousePress联合使用,按下鼠标的同时也会弹起鼠标。
- checkButtonsArgument:检查按钮的标志组合
- peer.mouseRelease(buttons):调用window底层,在当前屏幕将buttons按钮弹起
- afterEvent:生成事件后调用,见下文
public synchronized void mouseRelease(int buttons) {
checkButtonsArgument(buttons);
peer.mouseRelease(buttons);
afterEvent();
}
11.checkButtonsArgument:检查按钮参数
private void checkButtonsArgument(int buttons) {
if ( (buttons|LEGAL_BUTTON_MASK) != LEGAL_BUTTON_MASK ) {
throw new IllegalArgumentException("Invalid combination of button flags");
}
}
12.mouseWheel:鼠标滚动事件,wheelAmt为鼠标滚动的刻度,如果wheelAmt大于0则向上滚动,如果小于0则向下滚动,例如向上滚动一个刻度,则robot.mouseWheel(1)
public synchronized void mouseWheel(int wheelAmt) {
peer.mouseWheel(wheelAmt);
afterEvent();
}
13.keyPress:按钮按下事件(此类所有int入参都是ASCII进制码)
public synchronized void keyPress(int keycode) {
checkKeycodeArgument(keycode);
peer.keyPress(keycode);
afterEvent();
}
14.keyRelease:按钮弹起事件
public synchronized void keyRelease(int keycode) {
checkKeycodeArgument(keycode);
peer.keyRelease(keycode);
afterEvent();
}
15.checkKeycodeArgument:检测按钮是否定义
private void checkKeycodeArgument(int keycode) {
if (keycode == KeyEvent.VK_UNDEFINED) {
throw new IllegalArgumentException("Invalid key code");
}
}
16.getPixelColor:返回给定坐标屏幕的颜色
public synchronized Color getPixelColor(int x, int y) {
Color color = new Color(peer.getRGBPixel(x, y));
return color;
}
17.创建包含从屏幕读取的像素的图像。这张照片确实如此不包括鼠标光标
@param screenRect 要在屏幕坐标中捕获,
checkScreenCaptureAllowed:检测当前设备是否被允许读取屏幕像素:不能则会抛出异常
checkValidRect:检测screeRect宽度和高度,如果screeRect宽度和高度小于等于0,则抛出IllegalArgumentException
后半部分为读取图像
public synchronized BufferedImage createScreenCapture(Rectangle screenRect) {
checkScreenCaptureAllowed();
checkValidRect(screenRect);
BufferedImage image;
DataBufferInt buffer;
WritableRaster raster;
if (screenCapCM == null) {
/*
* Fix for 4285201
* Create a DirectColorModel equivalent to the default RGB ColorModel,
* except with no Alpha component.
*/
screenCapCM = new DirectColorModel(24,
/* red mask */ 0x00FF0000,
/* green mask */ 0x0000FF00,
/* blue mask */ 0x000000FF);
}
// need to sync the toolkit prior to grabbing the pixels since in some
// cases rendering to the screen may be delayed
Toolkit.getDefaultToolkit().sync();
int pixels[];
int[] bandmasks = new int[3];
pixels = peer.getRGBPixels(screenRect);
buffer = new DataBufferInt(pixels, pixels.length);
bandmasks[0] = screenCapCM.getRedMask();
bandmasks[1] = screenCapCM.getGreenMask();
bandmasks[2] = screenCapCM.getBlueMask();
raster = Raster.createPackedRaster(buffer, screenRect.width, screenRect.height, screenRect.width, bandmasks, null);
SunWritableRaster.makeTrackable(buffer);
image = new BufferedImage(screenCapCM, raster, false, null);
return image;
}
18.checkValidRect:检查坐标
private static void checkValidRect(Rectangle rect) {
if (rect.width <= 0 || rect.height <= 0) {
throw new IllegalArgumentException("Rectangle width and height must be > 0");
}
}
19.checkScreenCaptureAllowed:检查读取像素权限
private static void checkScreenCaptureAllowed() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkPermission(
SecurityConstants.AWT.READ_DISPLAY_PIXELS_PERMISSION);
}
}
20.afterEvent:事件后调用函数,此方法为内部方法
private void afterEvent() {
autoWaitForIdle();
autoDelay();
}
21.参数isAutoWaitForIdle的set和get方法
public synchronized boolean isAutoWaitForIdle() {
return isAutoWaitForIdle;
}
public synchronized void setAutoWaitForIdle(boolean isOn) {
isAutoWaitForIdle = isOn;
}
21.autoWaitForIdle:判定是否自动调用
private void autoWaitForIdle() {
if (isAutoWaitForIdle) {
waitForIdle();
}
}
22.此方法为autoDelay的set和get方法,如果你是用robot编写按键脚本,建议每次调用事件aotoDelay设置为一个随机数,防止被服务器检测出来
public synchronized int getAutoDelay() {
return autoDelay;
}
public synchronized void setAutoDelay(int ms) {
checkDelayArgument(ms);
autoDelay = ms;
}
23.delay:主线程休眠,其中ms为多少毫秒
public synchronized void delay(int ms) {
checkDelayArgument(ms);
try {
Thread.sleep(ms);
} catch(InterruptedException ite) {
ite.printStackTrace();
}
}
24.checkDelayArgument:延迟参数检测方法,该参数必须大于0且小于60s
private void checkDelayArgument(int ms) {
if (ms < 0 || ms > MAX_DELAY) {
throw new IllegalArgumentException("Delay must be to 0 to 60,000ms");
}
}
25:waitForIdle:等待事件队列中当前所有事件处理完毕
public synchronized void waitForIdle() {
checkNotDispatchThread();
// post a dummy event to the queue so we know when
// all the events before it have been processed
try {
SunToolkit.flushPendingEvents();
EventQueue.invokeAndWait( new Runnable() {
public void run() {
// dummy implementation
}
} );
} catch(InterruptedException ite) {
System.err.println("Robot.waitForIdle, non-fatal exception caught:");
ite.printStackTrace();
} catch(InvocationTargetException ine) {
System.err.println("Robot.waitForIdle, non-fatal exception caught:");
ine.printStackTrace();
}
}
26.checkNotDispatchThread:检测还未分派的线程,如果队里中存在,将抛出异常,Cannot call method from the event dispatcher thread(无法从事件调度程序线程调用方法)
private void checkNotDispatchThread() {
if (EventQueue.isDispatchThread()) {
throw new IllegalThreadStateException("Cannot call method from the event dispatcher thread");
}
}