SWT的底层设计

来源:互联网 发布:淘宝差评回复技巧 编辑:程序博客网 时间:2024/06/03 18:14
从前面的例子可以看出,swt是使用操作系统提供的本地部件库,通过Display对象来透明的与应用程序通信。Java控件对象的生命周期映射到本地控件的生命周期;当你创建了一个java空间,本地库也被创建,当java控件被销毁时,本地控件也被销毁。这个设计避免了调用一个代码对象的方法时,底层部件仍没有被创建的问题,这样的问题会导致代码控件生命周期和本地控件生命周期不匹配。
    例如,同Microsoft Foundation Classes (MFC)的两步创建过程相比。如果想创建一个按钮,要如下写代码:
CButton button; // Construct the C++ object on the stack
Button.Create( <parameters>); //Create the Windows widget
看一下在C++对象的构建代码和依赖的本地窗口工具代码之间插入代码:
CButton button; // Construct the C++ object on the stack
CString str = _T(“Hi”); // Create a CString to hold the button text
button.SetWindowText(str); // Set the button text—PROBLEM!
button.Create( <parameters>); // Create the Window widget
代码编译时没有任何问题,但是不能如期望运行。调试版本会引起一个断言,发布版本的行为是不确定的。
    </parameters> </parameters>
父控件
    很多GUIs在创建一个新控件前需要指明他的父控件,这个控件的生命周期是和他的父 控件相关的。父控件的生命时间就是子控件的生命时间。再有就是,很多本地控件都有他们的个性,或者说是“style”,必须在创建时指明。举例,一个按钮 可以是点击的,或者复选的。因为SWT控件在构造时创建相应的本地控件,所以必须将这些信息传递给他的构造函数。SWT控件一般都是带有两个参数的:父控 件和样式。父控件一般都是org.eclipse.swt.widgets.Widget或者他的一个子类。样式是在SWT类中定义的一些整形常量;可以 付给一个类型,或者使用或运算符添加几个类型。我们将在后面章节针对相应的控件指明它的类型。
   
释放控件
   
Swing的开发者可能会嘲笑这一点,作为证明SWT的缺点。Java开发者可能在这里也会感到讨厌和不适,而在这一点要说的是:自己处理的自己必须要清理。要知道,这是对java程序员的负责,对垃圾回收的侮辱。
    为 什么要释放对象?Java的垃圾回收可以很好的做到,但是GUI资源管理操作却承受巨大的约束。GUI资源是非常有限的,在很多平台上,系统资源是很有限的。由于SWT需要底层的图形资源来工作,每个SWT资源消耗着一个GUI资源,资源的延时释放不但对你的SWT程序有好处,同样对正在运行的GUI程序 有好处。Java的垃圾回收没有时间的保证,会使得对SWT的图形资源的管理变得很糟。所以,作为程序员的你必须承担起这个责任。
    这个工作有多繁重?实际上,并没有多大的工作量。在SWT的一系列文章上,Carolyn Macleod和Steve Northover给出了两个简单的法则来指导你(www.eclipse.org/articles/swt-design-2/swt-design-2.html):
•如果你创建了他,那么就要释放他
•释放了父控件,子控件也被释放
    法则一:如果你创建了他,那么就要释放他
    在本章节的前一部分,你知道了创建SWT控件,相应的本地控件也被创建了。也就是说,调用了SWT的构造函数,底层的本地资源也被创建了。所以如果你写下如下代码,那么你就创建了一个SWT颜色对象,那么也就在系统的底层资源里分配了一个颜色资源:
Color color = new Color(display, 255, 0, 0); // Create a red Color
法则一说明了如果你创建了他,那么你就要在使用完时释放他,像下面一样:
color.dispose(); // I create it, so I dispose it
但是,如果你没有使用构造函数来申请资源,你就不能显式的释放他。比如,考虑下面的代码:
Color color = display.getSystemColor(SWT.COLOR_RED); // Create a red Color
再一次获得了底层平台的颜色资源,但是没有分配他。法则一就说不能释放他。为什么不呢?因为这个不属于你-你只是借用了这个资源,其他的对象有可能正在使用它或者将要使用它。释放这个资源会变得非常严重的。
    法则二:释放了父控件,子控件也被释放
    对每一个使用new创建的控件都使用dispose ()会变得非常繁琐,会使得SWT很快被抛弃。但是,SWT的设计者意识到了这一点,创建了一种逻辑上层叠的自动释放机制。这意味着,当一个Shell被 释放了,所有与Shell有关的控件都被自动释放了。你会发现,永远也不会使用label.dispose()在“Hello World”程序里,甚至你使用了new来创建一个Label对象。当用户关闭了Shell,Label控件自动的释放了。
也许你在想,你永远不需要调用dispose(),这部分代码只是在浪费空间。实际上,有可能写一些程序,所有的资源都只有一个父类,并且这些资源都会自动释放。但是,考虑一下下面的改变Text中字体的代码。如下:
Text text = new Text(shell, SWT.BORDER); //Create the text field
Font font = new Font(display, ”Arial”, 4, SWT.BOLD); //Create the new font
text.setFont(font); //Set the font into the text field
这 个字体对象创建时没有父类,所以不会被自动释放,甚至当Shell被关闭了,Text对象调用了dispose()。也许你会对使用font这个负担而发 怒,但是考虑到text没有理由要对font负责-font不属于他。实际上,你也许会使用这个Font对象在各种各样的控件上,自动释放会引发一系列的 问题。
    忽略释放对象
    聪明的读者也许会发现在生命周期映射上有一个漏洞:如果Java包裹的本地控 件依然在活动,而他所属于的Shell被释放了,将发生什么情况呢?或者手动调用控件的dispose方法会怎么样呢?本地控件会被释放吗?我们是否可以 调用java对象而他的底层本地控件不存在?
答案当然是:可以!如果你调用一个对象而他的本地控件已经释放,会引出一些麻烦。一旦一个控件被释放 了,甚至他依然在活动,对他做什么都没有用。是的,Java对象依然可用,但是底层的对应点已经释放了。那么就会得到一个SWTException,显示 为“Widget has been disposed”。考虑下面的代码。 
import org.eclipse.swt.*;  import org.eclipse.swt.layout.*;  import org.eclipse.swt.widgets.*;  public class Broken  {    public static void main(String[] args)    {      Display display = new Display();      Shell shell = new Shell(display);      shell.setLayout(new RowLayout());      Text text = new Text(shell, SWT.BORDER);      shell.open();      while (!shell.isDisposed())      {         if (!display.readAndDispatch())         {            display.sleep();         }      }      System.out.println(text.getText()); // PROBLEM!      display.dispose();    }  }  


  代码可以编译和运行,但是在关闭了主窗口后控制台会打印出以下的错误:
Org.eclipse.swt.SWTException: Widget is disposed
at org.eclipse.swt.SWT.error(SWT.java:2332)
at org.eclipse.swt.SWT.error(SWT.java:2262)
at org.eclipse.swt.widgets.Widget.error(Widget.java:385)
at org.eclipse.swt.widgets.Control.getDisplay(Control.java:735)
at org.eclipse.swt.widgets.Widget.isValidThread(Widget.java:593)
at org.eclipse.swt.widgets.Widget.checkWidget(Widget.java:315)
at org.eclipse.swt.widgets.Text.getText(Text.java:705)
at Broken.main(Verison.java:24)
    需要注意的,当你在Windows XP上运行这个程序,你会得到一个对话框显示javaw.exe出现了问题,需要关闭,你是否愿意发送错误报告到Microsoft?
    总之,一旦一个对象被释放了,是否是显式的调用dispose()方法还是由于它的父控件被释放了,我们不用管他。