5.4 事件处理与线程安全性

AWT和Swing都是采用经典的事件处理模型。系统中的内部动作和用户的操作都会被抽象成为对应的事件,放入到一个事件队列中。这个队列中的事件会被依次进行处理。

5.4.1 事件处理

在AWT中的事件用java.awt.AWTEvent类来表示,而事件队列则由java.awt.EventQueue类来表示。EventQueue类中包含了对AWTEvent类的对象的处理方法,包括发布、获取、查看和分发事件等。事件队列的基本处理思路是:对于队列中当前存在的事件,按照顺序逐个调用dispatchEvent方法来分发这些事件。在分发的过程中就包括了对事件的处理,只不过实际的处理可能是由其他对象来完成的。

事件队列的一个实用功能是可以往其中发布自定义的事件,由事件队列来处理。代码清单5-8中给出了一个自定义事件的使用示例。当需要创建一个任意类型的自定义事件的时候,可以实现java.awt.ActiveEvent接口。当一个ActiveEvent接口的实现对象被分发时,其dispatch方法会被调用,相当于对该事件进行处理。通过EventQueue类的postEvent方法可以向事件队列中添加事件。由于EventQueue类要求队列中的事件对象都继承自AWTEvent类,所以自定义的事件类也需要继承自AWTEvent类。在一个AWTEvent类中需要包含两个要素:第一个是产生事件的源对象,可以是任意的Java对象;第二个是事件类型的标识符,它的值需要大于AWT为内部事件保留的最大标识符AWTEvent.RESERVED_ID_MAX,以避免与AWT定义的内部事件产生冲突。

代码清单5-8 自定义事件的使用示例


public class UseEventQueue{

private static class MyEvent extends AWTEvent implements ActiveEvent{

private String content;

public MyEvent(String content){

super(content, AWTEvent.RESERVED_ID_MAX+1);

this.content=content;

}

public void dispatch(){

System.out.println("字符串长度为:"+content.length());

}

}

public void useEventQueue(){

Frame frame=new Frame();

frame.setVisible(true);

Toolkit toolkit=Toolkit.getDefaultToolkit();

EventQueue queue=toolkit.getSystemEventQueue();

queue.postEvent(new MyEvent("Hello"));

}

}


除了自定义的事件之外,在AWT和Swing中,使用更多的是与组件相关的事件。EventQueue类在通过dispatchEvent方法来分发事件时,会根据事件的源对象类型和事件本身的类型来确定所采取的动作。比如前面提到的ActiveEvent类型的事件,处理的方式是直接调用该类型对象的dispatch方法;而对于源对象类型为AWT中的Component类和MenuComponent类的对象的事件来说,处理的方式是直接调用源对象的dispatchEvent方法,也就是说,对这类事件的处理由组件自己来完成;对于其他类型的事件,EventQueue类会直接忽略。除了使用系统提供的EventQueue类的对象之外,也可以自己创建EventQueue类的对象并使用。通过java.awt.Toolkit类可以与系统提供的事件队列进行交互。

当事件发生之后,AWT和Swing中对事件的处理采用了经典的监听者设计模式。在Component类及其子类中都有类似“add×××Listener(×××Listener)”这样的方法,用来对组件上相关事件添加处理器。比如,对组件上的鼠标事件感兴趣,可以通过addMouseListener方法来添加一个java.awt.event.MouseListener接口的实现对象。这样当组件上与鼠标相关的事件发生的时候,预先声明的处理器的对应方法就会被调用。这种事件处理模式在其他编程语言的用户界面组件库以及Web开发中也经常用到,开发人员应该都比较熟悉。×××Listener接口中通常包含了与某个主题相关的一系列需要实现的方法,比如MouseListener接口包括了鼠标单击、按下、释放、进入和离开等具体事件的处理方法。如果只对接口中的某几个方法感兴趣,那么可以继承自与该接口对应的×××Adapter类,并覆写其中的部分相关方法即可。结合前面提到的EventQueue类的实现,不难得出AWT中事件处理的基本流程。

当事件发生的时候,事件被添加到系统的事件队列中。该事件中包含了产生事件的源对象和相关的元数据。事件队列按照顺序依次对其中包含的事件进行处理。在处理的时候,如果发现这是一个Component类的对象上发生的事件,直接调用Component类的对象的dispatchEvent方法。而dispatchEvent方法会调用当前Component类的对象上已经注册的事件监听器中的方法,从而完成对事件的处理。对于一个Component类的对象,可以添加同一类事件的多个监听器。这些监听器中的方法被调用的先后顺序是不确定的。程序的处理逻辑不应该依赖特定的监听器调用顺序。各个监听器之间应该互相独立。