java SWT: 重写dispose()和DisposeListener的区别

24 篇文章 0 订阅

当需要在Widget被销毁(dispose)的时候执行一些比如释放资源的动作时,有两个途径来执行这些动作:
1. 重写dispose()方法,在dispose中释放资源的动作。
2. 添加DisposeListener,监听SWT.Dispose事件,在Listener中执行释放资源的动作。
应该用哪种方式呢?
答案是方法2,因为虽然都能执行dispose动作,但SWT隐式销毁对象的时候并不调用dispose()。

dispose()是提供给外部调用者显式销毁对象的方法,在应用程序正常关闭时逐层销毁组件对象时,是一种隐式销毁,并不调用dispose()方法。所以如果使用方法1,那么程序关闭的时候,并不会执行到你的代码。
使用DisposeListener临听SWT.Dispose事件,才能保证不论是显式销毁还是隐式销毁自己的代码都能被执行。

下面是测试代码:
MyComposite.java

package testwb;

import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.DisposeEvent;

public class MyComposite extends Composite {

    /**
     * Create the composite.
     * @param parent
     * @param style
     */
    public MyComposite(Composite parent, int style) {
        super(parent, style);
        addDisposeListener(new DisposeListener() {
            public void widgetDisposed(DisposeEvent e) {
                System.out.println("DisposeListener 被调用");
            }
        });

    }

    @Override
    protected void checkSubclass() {
        // Disable the check that prevents subclassing of SWT components
    }

    @Override
    public void dispose() {
        System.out.println("dispose 被调用");
        super.dispose();
    }

}

TestDispose.java

package testwb;

import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.SWT;

public class TestDispose {

    protected Shell shell;

    /**
     * Launch the application.
     * @param args
     */
    public static void main(String[] args) {
        try {
            TestDispose window = new TestDispose();
            window.open();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Open the window.
     */
    public void open() {
        Display display = Display.getDefault();
        createContents();
        shell.open();
        shell.layout();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
    }

    /**
     * Create contents of the window.
     */
    protected void createContents() {
        shell = new Shell();
        shell.setSize(450, 300);
        shell.setText("SWT Application");

        MyComposite myComposite = new MyComposite(shell, SWT.NONE);
        myComposite.setBounds(46, 20, 64, 64);
    }
}

在eclipse中调试运行TestDispose.java,并给MyComposite.java的System.out.println("DisposeListener 被调用");这一行加上断点。
关闭程序的时候,就会执行到断点行,下面是调用堆栈:
这里写图片描述
上图中红框标的部分可以看出,shell在执行关闭动作时会调用releaseChildren来销毁它的子组件,在销毁每个子widget的时候,调用的是widget的release()方法(默认访问属性,外部代码不可访问),release()中先调用sendEvent 发送SWT.Dispose事件给所有的Listener,再调用自己的releaseChildren()方法,一层层向下销毁(递归)所有widget。
所以可以发现这个销毁过程根本没有dispose()方法什么事儿。

Widget.release() 方法代码如下

void release (boolean destroy) {
    if ((state & DISPOSE_SENT) == 0) {
        state |= DISPOSE_SENT;
        sendEvent (SWT.Dispose);
    }
    if ((state & DISPOSED) == 0) {
        releaseChildren (destroy);
    }
    if ((state & RELEASED) == 0) {
        state |= RELEASED;
        if (destroy) {
            releaseParent ();
            releaseWidget ();
            destroyWidget ();
        } else {
            releaseWidget ();
            releaseHandle ();
        }
    }
}

当显式调用dispse()方法销毁对象的时候,dispose()还是调用release()方法来执行销毁动作。
Widget.dispose()方法的代码如下:

public void dispose () {
    /*
    * Note:  It is valid to attempt to dispose a widget
    * more than once.  If this happens, fail silently.
    */
    if (isDisposed ()) return;
    if (!isValidThread ()) error (SWT.ERROR_THREAD_INVALID_ACCESS);
    release (true);
}

再回头看看dispose()的javaDoc原文说明:

void org.eclipse.swt.widgets.Widget.dispose()
Disposes of the operating system resources associated with the receiver and all its descendants. After this method has been invoked, the receiver and all descendants will answer true when sent the message isDisposed(). Any internal connections between the widgets in the tree will have been removed to facilitate garbage collection. This method does nothing if the widget is already disposed.
NOTE: This method is not called recursively on the descendants of the receiver. This means that, widget implementers can not detect when a widget is being disposed of by re-implementing this method, but should instead listen for the Dispose event.

注意NOTE部分:该方法不会被子组件递归调用,所以widget被销毁的时候不能通过重写此方法检测到,应该用检测Dispose事件代替。

参考:
Widget.html.dispose()
http://help.eclipse.org/luna/nftopic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/widgets/Widget.html#dispose()

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

10km

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值