“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
事件驱动的抽象几乎完全隐藏了这种体系结构。 我们只在触发事件( 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); } }); } }
该程序为我们提供了如下调用堆栈:
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.h
和awt_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上可以看到活动的顶端。