“GetMessage()”循环的Java Swing对应位置在哪里?

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

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

概观

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

  Client Listeners (Us) ▲ │ events dispatched to client code ╭ ◀─────────┴───────────╮ │ Event Dispatch Thread │ ╰───────────┬─────────▶ ╯ │ events pulled from the queue ▼ Event Queue ▲ │ events posted to the queue ╭ ◀─────────┴───────────╮ │ WToolkit Thread │ ╰───────────┬─────────▶ ╯ │ messages pulled via PeekMessage ▼ Windows API 

事件驱动的抽象几乎完全隐藏了这种体系结构。 我们只在触发事件( actionPerformedpaintComponent等)时偶尔与最顶端进行交互,并偶尔自己发布事件( invokeLaterrepaint等)。

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

事件派遣线程

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

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

 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 

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

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

pumpEvents将我们pumpEventsForFilter包含外部循环逻辑的pumpEventsForFilter

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

然后将一个事件从队列中拉出并发送出来以便在pumpOneEventForFilters进行调度:

 void pumpOneEventForFilters(...) { AWTEvent event = null; ... try { ... EventQueue eq = getEventQueue(); ... event = eq.getNextEvent(); ... eq.dispatchEvent(event); ... } catch(...) { ... } ... } 

java.awt.EventQueue包含逻辑,其中事件的类型变窄并且事件被进一步分派。 dispatchEvent调用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调用dispatchEventImpl ,对于大多数侦听器类型的事件,它调用processEvent ,其中事件被缩小并再次转发:

 /** * Processes events occurring on this component. By default this * method calls the appropriate processEvent * 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使用鼠标事件来更改按钮模型的按下状态。 最后,按钮模型确定它是否已在setPressed单击,触发ActionEvent并调用我们的侦听器的actionPerformed

原生邮件

由于源无法在线获取,因此有点难以挖掘。 您需要下载OpenJDK源代码 。

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

  • Java: src/windows/classes/sun/awt/windows
  • Native: src/windows/native/sun/windows

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

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

这将我们AwtToolkit位于awt_Toolkit.hawt_Toolkit.cpp的C ++ AwtToolkit类(其他类遵循相同的文件名约定)。

eventLoop本机实现调用AwtToolkit::MessageLoop

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

AwtToolkit::CommonPeekMessageFunc调用PeekMessage ,这是GetMessage的非阻塞替代。)

这是外环所在的位置:

 UINT AwtToolkit::MessageLoop(IDLEPROC lpIdleFunc, PEEKMESSAGEPROC lpPeekMessageFunc) { ... m_messageLoopResult = 0; while (!m_breakMessageLoop) { (*lpIdleFunc)(); PumpWaitingMessages(lpPeekMessageFunc); /* pumps waiting messages */ ... } ... } 

PumpWaitingMessages有一个PumpWaitingMessages熟悉的消息循环:

 /* * 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; ... while (!m_breakMessageLoop && (*lpPeekMessageFunc)(msg)) { ... ::TranslateMessage(&msg); ::DispatchMessage(&msg); } ... } 

(并记得DispatchMessage调用WindowProc回调。)

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

WindowProc入口点似乎位于AwtFrame::WindowProc ,它委托超类层次结构。 大多数消息由AwtComponent::WindowProc处理。 例如, AwtComponent::WindowProc包含以下开关案例:

 case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: mr = WmMouseDown(static_cast(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上可以看到活动的顶端。