SWT中Shell关闭导致后续事件丢失的原因及解决方法咨询
关闭Shell导致后续事件丢失的原因与解决方案
问题复现
示例代码
public class Test { private static Listener listener = e -> { String type = null ; switch ( e.type ) { case SWT.MouseDown -> type = "MouseDown" ; case SWT.Activate -> type = "Activate" ; case SWT.Deactivate -> type = "Deactivate" ; } System.out.println( type + " - " + e ) ; } ; public static void main(String[] args) { Shell shell1 = new Shell() ; shell1.setText( "1" ) ; shell1.setSize( 300, 300 ) ; shell1.setLayout( new FillLayout() ) ; Composite composite1 = new Composite( shell1, SWT.NONE ) ; Shell shell2 = new Shell() ; shell2.setText( "2" ) ; shell2.setSize( 300, 300 ) ; shell2.setLayout( new FillLayout() ) ; Composite composite2 = new Composite( shell2, SWT.NONE ) ; addListeners( shell1 ) ; addListeners( composite1 ) ; addListeners( shell2 ) ; addListeners( composite2 ) ; // shell2.addListener( SWT.Deactivate, e -> shell2.close() ) ; shell1.open() ; shell2.open() ; Display display = shell1.getDisplay() ; while ( !shell1.isDisposed() ) { if ( !display.readAndDispatch() ) { display.sleep() ; } } display.dispose() ; } private static void addListeners( Control control ) { control.addListener( SWT.MouseDown, listener ) ; control.addListener( SWT.Activate, listener ) ; control.addListener( SWT.Deactivate, listener ) ; } }
正常事件流(未添加关闭逻辑)
当shell2处于激活状态,点击composite1时,事件流完整:
Activate - Event {type=26 Composite {} [layout=null] time=483235453 data=null x=0 y=0 width=0 height=0 detail=3} Deactivate - Event {type=27 Shell {2} [layout=FillLayout {type=SWT.HORIZONTAL}] time=483235453 data=null x=0 y=0 width=0 height=0 detail=0} Deactivate - Event {type=27 Composite {} [layout=null] time=483235453 data=null x=0 y=0 width=0 height=0 detail=0} Activate - Event {type=26 Shell {1} [layout=FillLayout {type=SWT.HORIZONTAL}] time=483235453 data=null x=0 y=0 width=0 height=0 detail=3} MouseDown - Event {type=3 Composite {} [layout=null] time=483236000 data=null x=187 y=115 width=0 height=0 detail=0}
异常事件流(添加关闭逻辑)
添加shell2.addListener( SWT.Deactivate, e -> shell2.close() )后,执行相同操作,事件流被截断:
Activate - Event {type=26 Composite {} [layout=null] time=483714125 data=null x=0 y=0 width=0 height=0 detail=3} Deactivate - Event {type=27 Shell {*Disposed*} [layout=null] time=483714125 data=null x=0 y=0 width=0 height=0 detail=0}
原因分析
- SWT事件同步派发特性:SWT的事件按顺序同步执行,在处理
shell2的SWT.Deactivate事件时,直接调用shell2.close()会立即触发Shell销毁流程,包括释放资源、移除Display事件队列中的关联上下文。 - 事件链被打断:此时系统正处于窗口激活切换的事件序列中,Shell销毁会干扰Display的事件派发逻辑,导致后续已入队的事件(如
shell1的Activate、MouseDown)因上下文被破坏而被丢弃。 - Dispose事件优先级:
shell.close()会触发SWT.Dispose事件,该事件的处理会打断当前事件链,终止后续事件的执行。
解决方案
核心是延迟执行Shell关闭操作,让当前事件序列完整处理完毕后再执行销毁逻辑,避免干扰事件派发。
方法:使用Display.asyncExec()延迟关闭
将关闭操作放入异步执行队列,确保当前所有已入队的事件处理完成后再关闭Shell:
shell2.addListener(SWT.Deactivate, e -> { shell2.getDisplay().asyncExec(() -> { if (!shell2.isDisposed()) { // 避免重复销毁 shell2.close(); } }); });
修改后,事件流会恢复完整,shell2会在所有激活切换、鼠标点击事件处理完成后再关闭。
内容的提问来源于stack exchange,提问作者Bourbon_7
相关产品推荐
相关产品推荐

