我是靠谱客的博主 俊秀面包,最近开发中收集的这篇文章主要介绍java dispatch类_Java Event Dispatch,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

Java Event Dispatch ----------Java Event Dispatch之旅-------------

探讨当EventQueue中的Event被EventDispatchThread拿出来之后的dispatch过程。 不管是什麽Event,在它被丢往EventQueue时,有两个Event的属性已经被决定(id和source),而EventDispatchThread会根据Event的source来决定接下来要dispatch的目标,也就是发生Event的AWT元件。dispatch的目标分成两类,一个是Component,另一个则是MenuComponent,在此仅以Component为例。

既然目标是Component,那麽首先当然是找Component.java囉!由于Event最后是由source的dispatchEvent(event)这个method开始的,所以就从dispatchEvent下手,结果发现dispatchEvent会再呼叫dispatchEventImpl(event),从这个method当中,我们看到了Java对于不同的Event可能会有不同的处理方式,为了简化起见,笔者只针对正常Event Model(normal-processing)的处理流程来作说明(也就是透过processEvent),部分code如下。

void dispatchEventImpl(AWTEvent e) {

// 3. Deliver event for normal processing

if (newEventsOnly) {

if (eventEnabled(e)) {

processEvent(e);

}

}

else if…

}

而processEvent会根据不同的Event物件继续dispatch的工作,可能继续被呼叫到的方法为processXXXEvent(…),例如: processFocusEvent(...)、processMouseEvent(...)processMouseMotionEvent(...)、processKeyEvent(...)、processComponentEvent(...)。

processEvent的部分code如下:

protected void processEvent(AWTEvent e) {

if (e instanceof FocusEvent) {

processFocusEvent((FocusEvent)e);

} else if (e instanceof KeyEvent) {

processKeyEvent((KeyEvent)e);

}else if(…)

}

接下来我们以processMouseEvent为例,code如下:

protected void processMouseEvent(MouseEvent e) {

MouseListener listener = mouseListener;

if (listener != null) { //检查是否有已经注册过的listener

int id = e.getID();

switch(id) {

case MouseEvent.MOUSE_PRESSED:

listener.mousePressed(e);

break;

case MouseEvent.MOUSE_RELEASED:

listener.mouseReleased(e);

break;

case MouseEvent.MOUSE_CLICKED:

listener.mouseClicked(e);

break;

case MouseEvent.MOUSE_EXITED:

listener.mouseExited(e);

break;

case MouseEvent.MOUSE_ENTERED:

listener.mouseEntered(e);

break;

}

}

}

看到这裡,不知道各位读者是否有那拨云见日的感觉,原来从dispatchEvent一路下来,一直到processXXXEvent才会透过已经被注册过的listener来呼叫对应的Event-Handler。这也就是为何在使用Event时,必须先以addXXXlistener来注册的原因了。再来,我们看看Listener如何在Source达成注册的动作!在Component.java中,有好几个用来注册listener的addXXXlistener方法如: addComponentListener、addFocusListener、addKeyListener和addMouseListener…等,因为Component是所有AWT元件的老祖宗,所以这些用来注册的方法,其实代表着所有继承自Component的元件,都能透过Event Delegation Model来handle这些Events(这其实就是物件继承的优势)。由于这些addXXXlistener的行为大致相同,所以我们就随便挑一个来看囉,就addMouseListener好了:

public synchronized void addMouseListener(MouseListener l) {

if (l == null) {

return;

}

mouseListener = AWTEventMulticaster.add(mouseListener,l);

newEventsOnly = true; //透过normal-processing的Event,newEventsOnly

//必须被设为true

…//以下有一些是跟lightweight component相关的code

…//不在本篇讨论范围内,所以就不多说囉!

}

嘿嘿…又有新的类别出现了,叫做”AWTEventMulticaster”,相信各位使用Java Event写过程式的读者,应该会听过Java 1.1之后的Event Model是採用Multicast的方式来通知处理事件的Event-Handler,没错,就是它啦!当元件(Source)上有事件产生时,该Source会以Multicast的方式通知所有曾经在该Source注册过的Listeners来呼叫对应的Event-Handler。透过AWTEventMulticaster的add静态方法,会把所要注册的Listener加到Source的xxxListener(用来判断是否有listener被注册)中,最后再由processXXXEvent来呼叫到对应的Event-Handler。至于实际注册的过程,当执行add方法时,会再呼叫到addInternal(..)

方法:

protected static EventListener addInternal(EventListener a,EventListener b) {

if (a == null) return b;

if (b == null) return a;

return new AWTEventMulticaster(a, b);

}

我们可以看到当注册的Listener只有一个时,会直接把该Listener传回,否则将会有一个AWTEventMulticaster的物件被产生并传回给Source的xxxListener,至于AWTEventMulticaster的物件为何可以传回给xxxListener呢?主要是因为AWTEventMulticaster实作了(implements)所有的Event Listeners。举例来

说,当只有一个ListenerA透过addMouseListener来注册,那麽透过AWTEventMulticaster的add(..)会直接传回ListenerA给Source的mouseListener。如果有两个Listeners注册时,会先产生一个AWTEventMulticaster物件,并把这两个注册的Listeners分别放到物件的a与b(型别为EventListener),code如下:

protected final EventListener a, b;

protected AWTEventMulticaster(EventListener a, EventListener b) {

this.a = a; this.b = b;

}

最后再把AWTEventMulticaster物件传回给Source的mouseListener。如果有3个Listeners要注册时,原理同上,只是此时要传回之AWTEventMulticaster物件的a和b之中会有一个是单纯的Listener物件,另一个则是AWTEventMulticaster物件…以此类推之。当所有注册手续完成,那麽就可以透过之前提及的processXXXEvent

来呼叫对应的Event-Handler了。

基本上Event的dispatch流程笔者就讲到这儿囉,最后同样用一张图(图5)来解释这整个过程:

图5 (略)

从以上的说明,相信各位应该已经清楚Event最后究竟往哪裡去了!不过在图5中还有一道关卡笔者尚未交代清楚,那就是怎样的Event才算是enabled Event呢?关于这部分将在稍后揭晓。

非事件委派者(Event-Listener)不可吗?

----------------------------------

从以上的讨论,各位读者应该对于Java 1.1之后所採用的Event-DelegationModel有了更进一步的认识,其实说穿了,”就是将Event委託/委派(delegate)给Event-Listeners的Event-Handlers来处理,而这个委託的动作必须是透过addXXXListener(Listener)的注册手续”。但是难道就一定得透过事件委派者(Event-Listener)不可吗?如果Java Programmer硬是不愿用Event- Delegation这套方式,那Event是不是就无从处理了呢? 当然不是。既然没有委派任何事件处理者,那就自己来吧! 找不到人帮忙时,也只好自力救济囉! :)

从Event dispatch的流程中,我们知道Event离开EventQueue之后,第一站便是Source(指发生Event的Component)的dispatchEvent方法,既然是Source本身先接到要处理的Event,那麽实在没理由一定得透过其他的Listeners,由Source自己拦下Event不就得了。从图5中,Event最后会被processXXXEvent来决定是否将Event丢给适当的Listener (by multicast),如果当下没有任何注册过的Listeners,那麽该Event就不被处理,因为此时的Event类别已经确定,只要再根据Event的Type(id)来个别处理即可,所以processXXXEvent正是拦截Event的适当位置。接下来笔者就以拦截WindowEvent的WINDOW_CLOSING来结束程式的例子做说明。

有写过Java GUI程式的读者们,一定都知道程式的结束并不是单纯按下视窗右上角的X按钮就可以的,而必须由Programmer自行拦截WindowEvent,并呼叫System.exit(0)来结束程式(注3)。不过为了跟标准的Event-DelegationModel做区别,在此笔者就以Override的方式由Source Component自行拦截处理Event。程式码(MyFrame.java)如下:

public class MyFrame extends Frame { public MyFrame() { enableEvents(AWTEvent.WINDOW_EVENT_MASK); this.setBounds(10,10,200,200); this.setVisible(true); } public static void main(String[] args) { MyFrame myFrame1 = new MyFrame(); } // Overridden so we can exit on System Close protected void processWindowEvent(WindowEvent e) { super.processWindowEvent(e); if(e.getID() == WindowEvent.WINDOW_CLOSING) { System.exit(0); }

public class MyFrame extends Frame { public MyFrame() {enableEvents(AWTEvent.WINDOW_EVENT_MASK);this.setBounds(10,10,200,200);this.setVisible(true);}public static void main(String[] args) {MyFrame myFrame1 = new MyFrame();}// Overridden so we can exit on System Closeprotected void processWindowEvent(WindowEvent e) {super.processWindowEvent(e);if(e.getID() == WindowEvent.WINDOW_CLOSING) {System.exit(0);}}

public MyFrame() {

enableEvents(AWTEvent.WINDOW_EVENT_MASK);

this.setBounds(10,10,200,200);

this.setVisible(true);

}

public static void main(String[] args) {

MyFrame myFrame1 = new MyFrame();

}

//Overridden so we can exit on System Close

protected void processWindowEvent(WindowEvent e) {

super.processWindowEvent(e);

if(e.getID() == WindowEvent.WINDOW_CLOSING) {

System.exit(0);

}

}

}

这是一个很阳春的Java GUI程式,只new了一个Frame并显示出来,由于不打算採用Event-Delegation Model,所以不会有呼叫addWindowListener注册的动作。在此MyFrame直接override java.awt.Window的rocessWindowEvent,所以当WindowEvent发生时,Event便会被MyFrame的processWindowEvent拦截下来,如果该WindowEvent为WindowEvent.WINDOW_CLOSING,也就是使用者按下了X钮时,那麽便呼叫System.exit(0)来结束程式。

从这个Sample中可以发现,WindowEvent的Source为自己(MyFrame),而WindowEvent也是由MyFrame自己来处理,跟事件委派者一点关係也没有。此外,在processWindowEvent中又呼叫了java.awt.Window的processWindowEvent方法,基本上这是比较正规的写法,通常我们会先让WindowEvent循正常管道分派处理过后,再针对要拦截的WindowEvent作特殊处理,否则的话,其他以Event- Delegation Model运作的WindowEvent,将无法被送到适当的Event-Listeners来处理(当然也可以先针对要拦截的WindowEvent作处理,再循正常管道分派处理)。拦截WindowEvent的过程如图6:

图6 (略)

再来必须跟各位读者交代的是,上一个单元最后提到:究竟什麽样的Event才算是enabled Event呢? 从Event Dispatch的过程中,如果该Event没有被enabled,那麽Event将无法继续被处理(不管是透过Event-Delegation Model或是Override的方式),而在Component.java中有个方法--eventEnabled,就是用来检查Event是否为enabled,该方法部分code如下:

boolean eventEnabled(AWTEvent e) { switch(e.id) { case ComponentEvent.COMPONENT_MOVED: case ComponentEvent.COMPONENT_RESIZED: case ComponentEvent.COMPONENT_SHOWN: case ComponentEvent.COMPONENT_HIDDEN: if ((eventMask&AWTEvent.COMPONENT_EVENT_MASK)!=0|| componentListener != null) { return true; } break; case … } }

boolean eventEnabled(AWTEvent e) {switch(e.id) {case ComponentEvent.COMPONENT_MOVED:case ComponentEvent.COMPONENT_RESIZED:case ComponentEvent.COMPONENT_SHOWN:case ComponentEvent.COMPONENT_HIDDEN:if ((eventMask&AWTEvent.COMPONENT_EVENT_MASK)!=0|| componentListener != null) { return true;} break; case … }}如果该Event为enabled则传回true,而Event是否为enabled可由两点决定:

(1)该Event所对应之Event Mask是否有被启动:(注4)

如果已经被启动,则发生Event的Source Component之eventMask跟对应的EVENT_MASK常数(定义在AWTEvent.java)做’&’运算后,必不为零。例如:

if(eventMask&AWTEvent.COMPONENT_EVENT_MASK)!=0)=>表示ComponentEvent所对应之Event Mask已被启动,为Enabled Event else=>反之则表示ComponentEvent不为Enabled Event

(2) 该Event所对应之Event-Listener是否存在:

也就是说,只要有对应的Event Listeners曾经注册过,那麽该Event便是Enabled Event。

现在我们再回到之前的Sample(MyFrame.java),由MyFrame的建构子中可以发现到enableEvents(AWTEvent.WINDOW_EVENT_MASK),相信现在各位读者已经有能力解释为何需要这段code了!因为MyFrame是透过Override的方式来处理WindowEvent,根本不会有WindowListener前来注册,WindowEvent自然也不会是Enabled Event,所以只好选择另一条路,透过enableEvents来启动WindowEvent的Event Mask,这才使得WindowEvent能够正常被处理。

最后提醒各位读者,儘管在Java中可以透过这种Override的方式来拦截Event,但是笔者在此并不鼓励各位读者使用这种方式,除非您有特殊的需求,例如设计JavaBeans元件。毕竟标准的Event-Delegation Model在使用上还是比较有弹性的,而且别忘了,Event-Delegation Model可是以multicast的方式来呼叫对应的Event-Handler喔,所以如果直接以Override的方式来处理Event,要达到同样的效果,恐怕要再多费点心思呢!所以囉,除非必要,不然的话,其实把Events都委派给其他Listeners来处理,用起来也挺顺的啦:)

注3:其实Win32系统通常也是要自行拦截WM_DESTROY讯息,接着呼叫PostQuitMessage函式将WM_QUIT讯息置于讯息伫列,当GetMessage遇上WM_QUIT时,才会离开讯息迴圈,接着结束程式。

注4:”启动Event Mask”指的是呼叫enableEvents方法,并传入要enable之EventMask,Ex: enableEvents(AWTEvent.WINDOW_EVENT_MASK),就是用来启动WindowEvent的Event Mask。

结论

----

有关Java Event的讨论就到此告一落了,限于篇幅,所以笔者主要只选择了”Event的来源”以及”Event dispatch的流程”等基本议题向各位读者做个报告,虽然这些议题其实都不难,不过却是学习Java Event的我们所不可不知的。除此之外,给各位读者一个小小的建议,如果可以的话,花点时间看看SUN的Class Library Source Code(安装JDK就有了),这对您绝对会有帮助的,而笔者在这篇文章中所写的内容,其实大半都是来自Trace的心得喔!

…嗯…好啦…谢谢各位的捧场,希望这篇文章能对各位读者有所帮助,谢谢!

最后

以上就是俊秀面包为你收集整理的java dispatch类_Java Event Dispatch的全部内容,希望文章能够帮你解决java dispatch类_Java Event Dispatch所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(50)

评论列表共有 0 条评论

立即
投稿
返回
顶部