"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
这种架构几乎完全被事件驱动的抽象所隐藏。我们仅在事件触发时(actionPerformed
、paintComponent
等)与最顶端进行交互,并且偶尔会自己发布事件(invokeLater
、repaint
等。 ).
关于这个主题的官方文档往往非常笼统,所以我将使用(非常解释的)源代码摘录。
事件调度线程
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 上可以看到该活动。
几年前我做过一些 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
这种架构几乎完全被事件驱动的抽象所隐藏。我们仅在事件触发时(actionPerformed
、paintComponent
等)与最顶端进行交互,并且偶尔会自己发布事件(invokeLater
、repaint
等。 ).
关于这个主题的官方文档往往非常笼统,所以我将使用(非常解释的)源代码摘录。
事件调度线程
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 上可以看到该活动。