"GetMessage()"循环的Java Swing对应物在哪里?

smw*_*dia 3 java swing awt

几年前我做了一些Win32 GUI编程.现在我正在使用Java Swing.

出于好奇,Win32消息循环逻辑的Swing对应物在哪里?在Win32中,它是通过API GetMessage()实现的.我想它肯定已被深深地包裹在某个地方.

Rad*_*def 11

概观

下图概括地说明了Swing/AWT在Windows平台上的工作原理:

       Our Listeners
             ?
             ? (Events dispatched to our code by EDT)
 ? ???????????????????????
 ? Event Dispatch Thread ?
 ??????????????????????? ?
             ? (Events pulled from the queue by EDT)
             ?
        Event Queue
             ?
             ? (Events posted to the queue by WToolkit)
 ? ???????????????????????
 ?    WToolkit Thread    ?
 ??????????????????????? ?
             ? (Messages pulled by WToolkit via PeekMessage)
             ?
        Windows API
Run Code Online (Sandbox Code Playgroud)

事件驱动的抽象几乎完全隐藏了这种体系结构.我们只与最顶层的最终互动时,事件被触发(actionPerformed,paintComponent,等)和偶尔发布自己的活动(invokeLater,repaint,等).

关于这个主题的官方文档往往非常一般,所以我将使用源代码中的(非常复述)摘录.

事件派遣线程

EDT是Swing事件处理线程,所有Swing程序主要在此线程上运行.在大多数情况下,这只是AWT系统,它位于java.awt.EventDispatchThread.

事件调度系统非常分散,所以我将通过一个特定的例子来假设JButton已经点击了.

为了开始弄清楚发生了什么,我们可能会看一下堆栈跟踪.

class ClickStack {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                JButton button = new JButton("Click for stack trace");

                button.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent ae) {
                        new Error().printStackTrace(System.out);
                    }
                });

                frame.add(button);
                frame.pack();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setVisible(true);
            }
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

该程序为我们提供了如下调用堆栈:

at sscce.ClickStack$1$1.actionPerformed
at javax.swing.AbstractButton.fireActionPerformed
...
at javax.swing.DefaultButtonModel.setPressed
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased
at java.awt.Component.processMouseEvent
...
at java.awt.Component.processEvent
...
at java.awt.Component.dispatchEventImpl
...
at java.awt.Component.dispatchEvent
at java.awt.EventQueue.dispatchEventImpl
...
at java.awt.EventQueue.dispatchEvent
at java.awt.EventDispatchThread.pumpOneEventForFilters
at java.awt.EventDispatchThread.pumpEventsForFilter
...
at java.awt.EventDispatchThread.pumpEvents
at java.awt.EventDispatchThread.run
Run Code Online (Sandbox Code Playgroud)

如果我们看看这个EventDispatchThread.run方法,我们会看到:

public void run() {
    try {
        pumpEvents(...);
    } finally {
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

EventDispatchThread.pumpEvents将我们带到EventDispatchThread.pumpEventsForFilter包含外部循环逻辑的内容:

void pumpEventsForFilter(...) {
    ...
    while(doDispatch && ...) {
        pumpOneEventForFilters(...);
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)

然后将一个事件从队列中拉出并发送给以下发送EventDispatchThread.pumpOneEventForFilters:

void pumpOneEventForFilters(...) {
    AWTEvent event = null;
    ...
    try {
        ...
        EventQueue eq = getEventQueue();
        ...
        event = eq.getNextEvent();
        ...
        eq.dispatchEvent(event);
        ...
    } catch(...) {
        ...
    } ...
}
Run Code Online (Sandbox Code Playgroud)

java.awt.EventQueue包含缩小事件类型并进一步调度事件的逻辑.EventQueue.dispatchEvent调用EventQueue.dispatchEventImpl我们看到以下决策结构:

if (event instanceof ActiveEvent) {
    ...
    ((ActiveEvent)event).dispatch();
} else if (src instanceof Component) {
    ((Component)src).dispatchEvent(event);
    ...
} else if (src instanceof MenuComponent) {
    ((MenuComponent)src).dispatchEvent(event);
} else if (src instanceof TrayIcon) {
    ((TrayIcon)src).dispatchEvent(event);
} else if (src instanceof AWTAutoShutdown) {
    ...
    dispatchThread.stopDispatching();
} else {
    ...
}
Run Code Online (Sandbox Code Playgroud)

我们熟悉的大多数事件都经历了这Component条路.

Component.dispatchEvent调用Component.dispatchEventImpl它,对大多数听众类型的事件,称Component.processEvent该事件被缩小,再进行转发:

/**
 * Processes events occurring on this component. By default this
 * method calls the appropriate process<event type>Event
 * method for the given class of event.
 * ...
 */
protected void processEvent(AWTEvent e) {
    if (e instanceof FocusEvent) {
        processFocusEvent((FocusEvent)e);
    } else if (e instanceof MouseEvent) {
        switch(e.getID()) {
          case MouseEvent.MOUSE_PRESSED:
          case MouseEvent.MOUSE_RELEASED:
          case MouseEvent.MOUSE_CLICKED:
          case MouseEvent.MOUSE_ENTERED:
          case MouseEvent.MOUSE_EXITED:
              processMouseEvent((MouseEvent)e);
              break;
          case ...:
              ...
        }
    } else if (e instanceof KeyEvent) {
        processKeyEvent((KeyEvent)e);
    } else if (e instanceof ComponentEvent) {
        processComponentEvent((ComponentEvent)e);
    } else if (...) {
        ...
    } ...
}
Run Code Online (Sandbox Code Playgroud)

对于JButton点击,我们跟随MouseEvent.

这些低级事件最终在内部有一个处理程序Component.例如,我们可能会看看javax.swing.plaf.BasicButtonListener哪些实现了许多侦听器接口.

BasicButtonListener使用鼠标事件来更改按钮模型的按下状态.最后,按钮模型确定它是否被点击DefaultButtonModel.setPressed,触发ActionEvent并且我们的监听器actionPerformed被调用.

原生消息

如何实现实际的本机窗口当然是特定于平台的,但我可以稍微浏览一下Windows平台,因为它就是你所问的内容.您将在以下目录中找到Windows平台的内容:

Windows实现java.awt.Toolkit,它为sun.awt.windows.WToolkit实际的消息循环启动一个单独的线程.WToolkit.run调用JNI方法eventLoop.源文件中的注释解释了:

/*
 * eventLoop() begins the native message pump which retrieves and processes
 * native events.
 * ...
Run Code Online (Sandbox Code Playgroud)

这导致我们进入C++ AwtToolkit类,位于awt_Toolkit.hawt_Toolkit.cpp(其他类遵循相同的文件名约定).

调用的本机实现eventLoopAwtToolkit::MessageLoop:

AwtToolkit::GetInstance().MessageLoop(AwtToolkit::PrimaryIdleFunc,
                                      AwtToolkit::CommonPeekMessageFunc);
Run Code Online (Sandbox Code Playgroud)

(AwtToolkit::CommonPeekMessageFunc调用PeekMessage,这是非阻塞的另一个自我GetMessage.)

这是外环所在的位置:

UINT
AwtToolkit::MessageLoop(IDLEPROC lpIdleFunc,
                        PEEKMESSAGEPROC lpPeekMessageFunc)
{
    ...

    m_messageLoopResult = 0;
    while (!m_breakMessageLoop) {

        (*lpIdleFunc)();

        PumpWaitingMessages(lpPeekMessageFunc); /* pumps waiting messages */
        ...
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)

AwtToolkit::PumpWaitingMessages实际上有一个熟悉的消息循环,调用TranslateMessageDispatchMessage:

/*
 * Called by the message loop to pump the message queue when there are
 * messages waiting. Can also be called anywhere to pump messages.
 */
BOOL AwtToolkit::PumpWaitingMessages(PEEKMESSAGEPROC lpPeekMessageFunc)
{
    MSG  msg;
    BOOL foundOne = FALSE;
    ...

    while (!m_breakMessageLoop && (*lpPeekMessageFunc)(msg)) {
        foundOne = TRUE;
        ProcessMsg(msg); // calls TranslateMessage & DispatchMessage (below)
    }
    return foundOne;
}

void AwtToolkit::ProcessMsg(MSG& msg)
{
    if (msg.message == WM_QUIT) {
        ...
    }
    else if (msg.message != WM_NULL) {
        ...

        ::TranslateMessage(&msg);
        ::DispatchMessage(&msg);
    }
}
Run Code Online (Sandbox Code Playgroud)

(并回想起那个DispatchMessageWindowProc回调的人.)

本机窗口由C++对象包装,该对象具有特定于平台的内容,以及Java代码中的一些API的松散并行.

似乎有几个WindowProc功能.一个只是由工具包内部使用AwtToolkit::WndProc,以及一个空窗口.

WindowProc我们真正感兴趣的功能是AwtComponent::WndProc.WndProc调用一个名为的虚函数AwtComponent::WindowProc.一些子类覆盖WindowProc(例如AwtFrame::WindowProc),但大多数消息由处理AwtComponent::WindowProc.例如,它包含以下开关案例:

case WM_LBUTTONDOWN:
case WM_LBUTTONDBLCLK:
    mr = WmMouseDown(static_cast<UINT>(wParam), myPos.x, myPos.y,
                     LEFT_BUTTON);
    break;
Run Code Online (Sandbox Code Playgroud)

AwtComponent::WmMouseDown开始了一系列的职位调用java.awt.MouseEventEventQueueJava中:

SendMouseEvent(java_awt_event_MouseEvent_MOUSE_PRESSED, now, x, y,
               GetJavaModifiers(), clickCount, JNI_FALSE,
               GetButton(button), &msg);
Run Code Online (Sandbox Code Playgroud)

活动结束后,我们最终被带回到EDT上可以看到活动的顶端.