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

Where is the Java Swing counterpart of "GetMessage()" loop?

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

出于好奇,Win32 消息循环逻辑的 Swing 对应物在哪里?在Win32中,它是用APIGetMessage()来实现的。我猜它一定被深深地包裹在什么地方。

概览

下图大致说明了 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

这种架构几乎完全被事件驱动的抽象所隐藏。我们仅在事件触发时(actionPerformedpaintComponent 等)与最顶端进行交互,并且偶尔会自己发布事件(invokeLaterrepaint 等。 ).

关于这个主题的官方文档往往非常笼统,所以我将使用(非常解释的)源代码摘录。

事件调度线程

EDT是Swing事件处理线程,all Swing programs run primarily on this thread. For the most part, this is just the AWT system and it's located in 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);
            }
        });
    }
}

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

at sscce.ClickStack.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

如果我们看一下 EventDispatchThread.run 方法,我们会看到:

public void run() {
    try {
        pumpEvents(...);
    } finally {
        ...
    }
}

EventDispatchThread.pumpEvents takes us to EventDispatchThread.pumpEventsForFilter 其中包含外层循环逻辑:

void pumpEventsForFilter(...) {
    ...
    while(doDispatch && ...) {
        pumpOneEventForFilters(...);
    }
    ...
}

然后事件从队列中拉出并在 EventDispatchThread.pumpOneEventForFilters:

中发送以进行调度
void pumpOneEventForFilters(...) {
    AWTEvent event = null;
    ...
    try {
        ...
        EventQueue eq = getEventQueue();
        ...
        event = eq.getNextEvent();
        ...
        eq.dispatchEvent(event);
        ...
    } catch(...) {
        ...
    } ...
}

java.awt.EventQueue contains logic where the type of event is narrowed and the event is further dispatched. EventQueue.dispatchEvent calls 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 {
    ...
}

我们熟悉的大多数事件都经过 Component 路径。

Component.dispatchEvent calls Component.dispatchEventImpl which, for most listener-type events, calls 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 (...) {
        ...
    } ...
}

对于 JButton 点击,我们正在关注 MouseEvent

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

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

本地消息传递

如何实现实际的原生 window 当然是特定于平台的,但我可以稍微了解一下 Windows 平台,因为这是你问的。您将在以下目录中找到 Windows 平台内容:

java.awt.Toolkit 的 Windows 实现,即 sun.awt.windows.WToolkit, starts a separate thread for the actual message loop. WToolkit.run calls a JNI method eventLoop. A comment in the source file 解释说:

/*
 * eventLoop() begins the native message pump which retrieves and processes
 * native events.
 * ...

这将我们带到位于 awt_Toolkit.h and awt_Toolkit.cpp 中的 C++ AwtToolkit class(其他 classes 遵循相同的文件名约定)。

native implementation of eventLoop calls AwtToolkit::MessageLoop:

AwtToolkit::GetInstance().MessageLoop(AwtToolkit::PrimaryIdleFunc,
                                      AwtToolkit::CommonPeekMessageFunc);

(AwtToolkit::CommonPeekMessageFunc calls PeekMessage, which is the non-blocking alter-ego of GetMessage.)

这是外循环所在的位置:

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

    m_messageLoopResult = 0;
    while (!m_breakMessageLoop) {

        (*lpIdleFunc)();

        PumpWaitingMessages(lpPeekMessageFunc); /* pumps waiting messages */
        ...
    }
    ...
}

AwtToolkit::PumpWaitingMessages actually has a familiar-looking message loop, which calls TranslateMessage and DispatchMessage:

/*
 * 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);
    }
}

(回想一下 DispatchMessage 调用 WindowProc 回调。)

原生 window 由一个 C++ 对象包装,该对象具有特定于平台的内容,以及我们在 Java 代码中的某些 API 的松散并行。

似乎有几个 WindowProc 函数。一个仅供工具包内部使用,AwtToolkit::WndProc, along with an empty window.

我们真正感兴趣的 WindowProc 函数是 AwtComponent::WndProc. WndProc calls a virtual function called AwtComponent::WindowProc. Some subclasses override WindowProc (e.g. AwtFrame::WindowProc),但大多数消息都由 AwtComponent::WindowProc 处理。例如,它包含以下开关案例:

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

AwtComponent::WmMouseDown 开始一系列调用,将 java.awt.MouseEvent 发布到 Java 中的 EventQueue:

SendMouseEvent(java_awt_event_MouseEvent_MOUSE_PRESSED, now, x, y,
               GetJavaModifiers(), clickCount, JNI_FALSE,
               GetButton(button), &msg);

活动发布后,我们最终会回到顶部,在 EDT 上可以看到该活动。