Где находится аналог Java Swing цикла "GetMessage()"?
Я занимался программированием Win32 GUI несколько лет назад. Сейчас я использую Java Swing.
Просто из любопытства, где аналог Swing логики цикла сообщений Win32? В Win32 это достигается с помощью API GetMessage (). Я думаю, это должно быть где-то глубоко завернуто.
1 ответ
обзор
Следующая диаграмма широко иллюстрирует, как 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, и все программы 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 {
...
}
}
EventDispatchThread.pumpEvents
берет нас к 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
содержит логику, в которой тип события сужается, а событие отправляется дальше. 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 {
...
}
Большинство событий, с которыми мы знакомы, проходят через 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 (...) {
...
} ...
}
Для JButton
нажмите, мы следуем за MouseEvent
,
Эти события низкого уровня, в конечном счете, имеют один внутренний обработчик Component
, Так, например, мы могли бы взглянуть на javax.swing.plaf.BasicButtonListener
который реализует ряд интерфейсов слушателя.
BasicButtonListener
использует события мыши для изменения нажатого состояния модели кнопки. Наконец, модель кнопки определяет, была ли она нажата в DefaultButtonModel.setPressed
увольняет ActionEvent
и наш слушатель actionPerformed
вызывается.
Native Messaging
То, как реализовано собственное собственное окно, конечно, зависит от платформы, но я могу немного пройтись по платформе Windows, так как это то, о чем вы спрашивали. Вы найдете материал для платформы Windows в следующих каталогах:
- Джава:
src/windows/classes/sun/awt/windows
- Родные:
src/windows/native/sun/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.
* ...
Это приводит нас к C++ AwtToolkit
класс, расположенный в awt_Toolkit.h
а также awt_Toolkit.cpp
(другие классы следуют тому же соглашению об имени файла).
Нативная реализация 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 */
...
}
...
}
AwtToolkit::PumpWaitingMessages
на самом деле имеет знакомый цикл сообщений, который вызывает TranslateMessage
а также 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
Перезвоните.)
Собственное окно обернуто объектом C++, который имеет специфические для платформы вещи, а также свободную параллель некоторых API, которые мы имеем в коде Java.
Там, кажется, есть пара 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;
AwtComponent::WmMouseDown
начинается серия звонков, которые публикует java.awt.MouseEvent
к EventQueue
в Java:
SendMouseEvent(java_awt_event_MouseEvent_MOUSE_PRESSED, now, x, y,
GetJavaModifiers(), clickCount, JNI_FALSE,
GetButton(button), &msg);
После того, как событие опубликовано, мы в конечном счете возвращаемся к вершине, где событие видно на EDT.