Java 中的事件是如何产生的?
How events are generated in Java?
首先我不是在询问事件处理。我知道处理是使用观察者模式实现的。
举个小例子。假设我在 JFrame 上有一个 Jbutton。我点击这个按钮的顶部。
现在按钮如何知道我点击了它?
1) 是否有java线程在等待点击?如果是这样,这段代码是从哪里来的(等到我被点击的部分)?那么每个摆动组件是否都在等待线程之上的事件?我认为这是一项非常昂贵的任务。
2) 如果不是,这是如何工作的?
Is there any java thread waiting for the click?
是的,它叫做事件调度线程 (EDT)。
由于 Swing 中的所有组件都是轻量级的,这意味着只是一个漂亮的动画,仅此而已,它实际上是顶级组件,比如说,跟踪鼠标点击并将其向上传递的 JFrame
到该像素上的组件。
Is there any java thread waiting for the click?
是的,java.awt.EventDispatchThread
。这里引用 java 教程中的 the event dispatch thread section :
Swing event handling code runs on a special thread known as the event
dispatch thread. Most code that invokes Swing methods also runs on
this thread. This is necessary because most Swing object methods are
not "thread safe": invoking them from multiple threads risks thread
interference or memory consistency errors. Some Swing component
methods are labelled "thread safe" in the API specification; these can
be safely invoked from any thread. All other Swing component methods
must be invoked from the event dispatch thread. Programs that ignore
this rule may function correctly most of the time, but are subject to
unpredictable errors that are difficult to reproduce.
要回答您的下一个问题,
If so from where does this code come from (wait till I get clicked
part)?
EventDispatchThread
启动一个永久 事件泵,并在其 run
方法中调用 pumpEvents(Conditional)
。
public void run() {
try {
pumpEvents(new Conditional() {
public boolean evaluate() {
return true;
}
});
} finally {
getEventQueue().detachDispatchThread(this);
}
}
任何事件处理程序都可以随时选择阻止此事件泵,但应该通过再次调用 pumpEvents(Conditional)
来启动一个新的泵(而不是新的 EDT)。一旦 Conditional
评估为 false
并且额外的 Event
被发送并调度,此辅助事件泵将自动退出。
泵事件将调用 AWTEvent#getNextEvent
,后者将从事件队列中检索事件。
Then Does each and every swing component is waiting for events on top
of threads? I assume this is a very expensive task.
同样,java 教程回答了这个问题
It's useful to think of the code running on the event dispatch thread
as a series of short tasks. Most tasks are invocations of
event-handling methods, such as ActionListener.actionPerformed. Other
tasks can be scheduled by application code, using invokeLater or
invokeAndWait. Tasks on the event dispatch thread must finish quickly;
if they don't, unhandled events back up and the user interface becomes
unresponsive.
事件在独立于平台的 class java.awt.EventQueue
中排队。事件按优先级存储在队列中,为每个优先级创建一个队列。
private static final int LOW_PRIORITY = 0;
private static final int NORM_PRIORITY = 1;
private static final int HIGH_PRIORITY = 2;
private static final int ULTIMATE_PRIORITY = 3;
Events are pulled off the EventQueue starting with the Queue of
highest priority. We progress in decreasing order across all Queues.
请注意,缓存一些经常调用的事件以获得更好的性能,例如 PaintEvent.Paint
、PaintEvent.UPDATE
、MouseEvent.MOUSE_MOVED
、MouveSevent.MOUSE_DRAGGED
。
这并没有你想象的那么贵。
观察者模式贯穿于整个堆栈:
- 用户释放鼠标按钮
- 鼠标向CPU发送消息,触发硬件中断
- 操作系统的中断处理程序意识到鼠标在按下按钮后没有移动,即发生了鼠标点击。它识别鼠标位置的 window 和负责 window 的应用程序,并将鼠标单击消息放入应用程序的事件队列
我们的 Swing 应用程序的 "event dispatch thread" 运行以下形式的循环:
while (!shutdownRequested) {
Event e = retrieveEventFromEventQueue(); // for instance our mouse clicked event
handleEvent(e);
}
在 AWT/Swing 中,有一个线程执行该代码。第一次调用将阻塞,直到有新事件可用,并且 handleEvent() 将调用此事件的侦听器。也就是说,单个线程执行所有 UI 更新(这就是为什么不应在事件侦听器中完成长 运行 任务的原因,因为这会冻结 ui),并且如果用户不与应用程序交互。
首先我不是在询问事件处理。我知道处理是使用观察者模式实现的。
举个小例子。假设我在 JFrame 上有一个 Jbutton。我点击这个按钮的顶部。 现在按钮如何知道我点击了它?
1) 是否有java线程在等待点击?如果是这样,这段代码是从哪里来的(等到我被点击的部分)?那么每个摆动组件是否都在等待线程之上的事件?我认为这是一项非常昂贵的任务。
2) 如果不是,这是如何工作的?
Is there any java thread waiting for the click?
是的,它叫做事件调度线程 (EDT)。
由于 Swing 中的所有组件都是轻量级的,这意味着只是一个漂亮的动画,仅此而已,它实际上是顶级组件,比如说,跟踪鼠标点击并将其向上传递的 JFrame
到该像素上的组件。
Is there any java thread waiting for the click?
是的,java.awt.EventDispatchThread
。这里引用 java 教程中的 the event dispatch thread section :
Swing event handling code runs on a special thread known as the event dispatch thread. Most code that invokes Swing methods also runs on this thread. This is necessary because most Swing object methods are not "thread safe": invoking them from multiple threads risks thread interference or memory consistency errors. Some Swing component methods are labelled "thread safe" in the API specification; these can be safely invoked from any thread. All other Swing component methods must be invoked from the event dispatch thread. Programs that ignore this rule may function correctly most of the time, but are subject to unpredictable errors that are difficult to reproduce.
要回答您的下一个问题,
If so from where does this code come from (wait till I get clicked part)?
EventDispatchThread
启动一个永久 事件泵,并在其 run
方法中调用 pumpEvents(Conditional)
。
public void run() {
try {
pumpEvents(new Conditional() {
public boolean evaluate() {
return true;
}
});
} finally {
getEventQueue().detachDispatchThread(this);
}
}
任何事件处理程序都可以随时选择阻止此事件泵,但应该通过再次调用 pumpEvents(Conditional)
来启动一个新的泵(而不是新的 EDT)。一旦 Conditional
评估为 false
并且额外的 Event
被发送并调度,此辅助事件泵将自动退出。
泵事件将调用 AWTEvent#getNextEvent
,后者将从事件队列中检索事件。
Then Does each and every swing component is waiting for events on top of threads? I assume this is a very expensive task.
同样,java 教程回答了这个问题
It's useful to think of the code running on the event dispatch thread as a series of short tasks. Most tasks are invocations of event-handling methods, such as ActionListener.actionPerformed. Other tasks can be scheduled by application code, using invokeLater or invokeAndWait. Tasks on the event dispatch thread must finish quickly; if they don't, unhandled events back up and the user interface becomes unresponsive.
事件在独立于平台的 class java.awt.EventQueue
中排队。事件按优先级存储在队列中,为每个优先级创建一个队列。
private static final int LOW_PRIORITY = 0;
private static final int NORM_PRIORITY = 1;
private static final int HIGH_PRIORITY = 2;
private static final int ULTIMATE_PRIORITY = 3;
Events are pulled off the EventQueue starting with the Queue of highest priority. We progress in decreasing order across all Queues.
请注意,缓存一些经常调用的事件以获得更好的性能,例如 PaintEvent.Paint
、PaintEvent.UPDATE
、MouseEvent.MOUSE_MOVED
、MouveSevent.MOUSE_DRAGGED
。
这并没有你想象的那么贵。
观察者模式贯穿于整个堆栈:
- 用户释放鼠标按钮
- 鼠标向CPU发送消息,触发硬件中断
- 操作系统的中断处理程序意识到鼠标在按下按钮后没有移动,即发生了鼠标点击。它识别鼠标位置的 window 和负责 window 的应用程序,并将鼠标单击消息放入应用程序的事件队列
我们的 Swing 应用程序的 "event dispatch thread" 运行以下形式的循环:
while (!shutdownRequested) { Event e = retrieveEventFromEventQueue(); // for instance our mouse clicked event handleEvent(e); }
在 AWT/Swing 中,有一个线程执行该代码。第一次调用将阻塞,直到有新事件可用,并且 handleEvent() 将调用此事件的侦听器。也就是说,单个线程执行所有 UI 更新(这就是为什么不应在事件侦听器中完成长 运行 任务的原因,因为这会冻结 ui),并且如果用户不与应用程序交互。