java Robot源码解析(史上最全)

  • Post author:
  • Post category:java


简介:此类用于生成本机系统输入事件,出于测试自动化、自运行演示和其他可以控制鼠标和键盘的应用程序

这是需要的。

机器人的主要目的是方便用户

Java平台实现的自动化测试

方法介绍及解析:以下private开头方法为Robot内部使用,方法,public方法为对外开放方法,请重点关注public方法

Robot中对外提供接口

使用要点

代码示例

说明
1.实例化方法 new Robot(); 无参实例化,详见1
new Robot(

GraphicsEnvironment.getLocalGraphicsEnvironment()
    .getDefaultScreenDevice());
带参实例化,详见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");
        }
    }



版权声明:本文为lijunliang2017原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。