分享
 
 
 

使用“惰性”评估提高 GUI 的性能

王朝other·作者佚名  2006-11-24
窄屏简体版  字體: |||超大  

摘要

缓慢的 GUI 是 Java 最大的性能问题之一。这篇 Java 技巧说明如何创建构造速度较快的 GUI,这是通过只构建必要的部件实现的。

缓慢的图形用户界面 (GUI) 是人们对 Java 常发的牢骚。尽管构造快速的 GUI 可能既费时又费力,但如果使用我提供的这个类,您只须做少量的工作就可以加快 GUI 的速度。对于彻底的 GUI 改进而言,这只是第一步;但是,这个类是极好的一种解决方案,因为它很简单。有时这个类对性能的提高足以满足您的需要,您无须做任何额外的工作。

方法

这种方法很容易表述:直到必要时才构建 GUI 组件。从宏观上讲,这是所有 Java 程序的默认工作方式。毕竟,谁会在初始化时就构造程序的全部可能的框架和窗口呢?但是,我在本文提供的这个类允许您进行更精细、更惰性的构造。

我构建的用来支持 GUI 组件惰性构造的这个实用工具类基于三点经验。第一点,大多数 GUI 面板都很简单,并且无需做任何额外的工作就可以很快完成初始化。

将大量 GUI 组件堆积到屏幕上的面板是大多数缓慢初始化的原因。这些面板主要属于两种类型,即带选项卡的面板或滚动面板。

第二点,通常直到要查看 GUI 组件时才需要构造它。第三点,只有在下列方法之一被调用之后 GUI 组件才是可见的:

public void paint (Graphics)

public void paintComponents(Graphics)

public void paintAll (Graphics)

public void repaint ()

public void repaint (long)

public void repaint (int, int, int, int)

public void repaint (long, int, int, int, int)

public void update (Graphics)

创建实用工具面板类的思想是,将大多数 GUI 构建代码移出初始化块和构造函数。我们将这些代码放入一个名为 lazyConstructor() 的函数中。这个函数完成平常由 GUI 构造函数完成的大多数工作,但它本身并不是构造函数。这个实用工具面板类确保每当调用任何绘制或更新方法之前都调用一次 lazyConstructor 方法。

在选项卡面板内使用这个面板类可以使带选项卡的面板更快地完成构造,因为最初只需构造可见的那个选项卡面板。

代码

该类的代码如下所示:

import java.awt.*;

/**

* LazyPanel 是一个抽象基类,它提供将 Panel 对象的置入操作延迟到

* 实际查看该面板时再进行的功能。当使用 CardLayout 和选项卡面板

* 视图时这个类极为有用,因为它允许根据您的操作构造子视图,而不必

* 一开始就等待全部构造完成。

*

* 如果子类选择覆盖下面的任一个方法,则它们必须保证被覆盖的方法

* 首先调用父类的方法。这些方法是:

*

* public void paint (Graphics)

* public void paintComponents(Graphics)

* public void paintAll (Graphics)

* public void repaint ()

* public void repaint (long)

* public void repaint (int, int, int, int)

* public void repaint (long, int, int, int, int)

* public void update (Graphics)

*

* 其中的每个方法都确保首先构造面板,然后便将调用转发给父类。

*

* 如果您要使用这个类,请首先创建它的一个子类,然后尽可能地

* 将子类构造函数中的代码移到 lazyConstructor 方法中。下面是

* 使用 LazyPanel 的一个示例:

*

*

*

* import java.awt.*;

*

* class BusyPanel extends LazyPanel

* {

* public BusyPanel (int rows, int cols)

* {

* this.rows = rows;

* this.cols = cols;

* }

*

* protected void lazyConstructor()

* {

* setLayout (new GridLayout (rows, cols));

* for (int i = 0; i < rows * cols; ++i)

* {

* add (new Button (Integer.toString (i + startValue)));

* ++startValue;

* }

* }

*

* static private int startValue = 0;

*

* private int rows;

* private int cols;

* }

*

* 您可以这样使用它:

*

* import java.awt.*;

* import java.awt.event.*;

*

* class TestFrame extends Frame

* {

* public TestFrame ()

* {

* setLayout (new BorderLayout ());

* add (nextButton, "South");

*

* framePanel.setLayout (layout);

* for (int i = 0; i < 30; ++i)

* framePanel.add (new BusyPanel (8, 8), "");

* add (framePanel, "Center");

*

* nextButton.addActionListener (

* new ActionListener()

* {

* public void actionPerformed (ActionEvent event)

* {

* layout.next (framePanel);

* }

* }

* );

*

* setSize (400, 300);

* }

*

* private CardLayout layout = new CardLayout();

* private Button nextButton = new Button ("Next Panel");

* private Panel framePanel = new Panel();

*

* static public void main (String args[])

* {

* (new TestFrame()).show();

* }

* }

*

* 要查看使用 LazyPanel 的优越性,请试着将 lazyConstructor() 方法中

* 的代码移到 BusyPanel 的构造函数中,重新编译该类,然后回头再看本例。

* 启动时的额外延迟就是 LazyPanel 类要消除的问题。

*

* 这种处理对 swing 同样有效。只须对 LazyPanel 稍作修改,让它

* 扩展 JPanel(而不是 Panel)即可。

*/

public abstract class LazyPanel extends Panel

{

// 我只希望调用 lazyConstructor 一次。

private boolean lazyConstructorCalled = false;

// Swing 的某些版本在将组件添加到它们的容器之前

// 调用 paint() 方法。我们不希望在显示组件之前

// 调用 lazyConstructor。

private boolean isConstructorFinished = false;

/**

* 创建一个 LazyPanel。

*/

protected LazyPanel ()

{

isConstructorFinished = true;

}

public void paint (Graphics g)

{

callLazyConstructor();

super.paint (g);

}

public void paintAll(Graphics g)

{

callLazyConstructor();

super.paintAll (g);

}

public void paintComponents (Graphics g)

{

callLazyConstructor();

super.paintComponents (g);

}

public void repaint ()

{

callLazyConstructor();

super.repaint();

}

public void repaint (long l)

{

callLazyConstructor();

super.repaint (l);

}

public void repaint (int i1, int i2, int i3, int i4)

{

callLazyConstructor();

super.repaint (i1, i2, i3, i4);

}

public void repaint (long l, int i1, int i2, int i3, int i4)

{

callLazyConstructor();

super.repaint (l, i1, i2, i3, i4);

}

public void update (Graphics g)

{

callLazyConstructor();

super.update (g);

}

/**

* 强制调用在子类中实现的 lazyConstructor() 方法。

* 如果多次调用某个给定对象的这个方法,则除第一次

* 调用以外的任何其他调用不会执行任何操作。

*/

public synchronized final void callLazyConstructor()

{

// 以下代码的大致思想如下所示:

// 1) 检查该方法是否被成功调用过。

// 如果是,只返回而不执行任何操作。

//

// 2) 否则 ... 调用惰性构造函数。

// 3) 调用 validate 以使所添加的全部组件变为可见的。

// 4) 注意我们已在运行的程序。

if ((lazyConstructorCalled == false) && (getParent() != null))

{

lazyConstructor();

lazyConstructorCalled = true;

validate();

}

}

/**

* 任何子类都必须实现这个方法。本应该

* 出现在子类构造函数中的大多数组件创建

* 代码将出现在此处。请参阅上面的

* 示例。

*/

abstract protected void lazyConstructor ();

}

使用 LazyPanel

LazyPanel 类的使用很简单,但您应该注意几点。

别在 lazyConstructor 中发出异常

对于面板,我们很容易编写在发生错误时发出异常的构造函数。不幸的是,lazyConstructor只是一个普通方法,而不是构造函数。因为 lazyConstructor 是从 paint() 中被调用的,所以它不能发出除 RuntimeExceptions 和 Errors 以外的任何异常。因为除了 AWT 线程之外再没有任何别的调用程序捕获这些异常和错误,所以最好不要发出任何异常。如有必要,您可以将 lazyConstructor 方法的主体放在一个 try/catch 块中,然后在这个 catch 块中进行一些敏感的操作。

GUI 编制器不合作

用 Inprise 公司的 JBuilder 或 Symantec 公司的 Cafe 中的 GUI 编制器生成的代码不能很好地与 LazyPanel 类配合使用。GUI 编制器生成的代码使用初始化函数为面板构造大多数 GUI 组件。这样就体现不出 LazyPanel 的优点,因为没有将任何操作移到 lazyConstructor() 方法中。

一种方法是手动将初始化函数中的组件构造代码移到 lazyConstructor 方法中。不幸的是,这样一来 GUI 编制器将不能再修改面板以作进一步的增强。更好的一种方法是保留 GUI 编制器生成的面板不动,而将它放入一个 LazyPanel 中。这样 LazyPanel 就只包含一个组件 -- GUI 编制器构建的组件。

代码的最终执行速度并没有加快

从最终结果来看,这个方法实际上并没有使应用程序加速。如果您的 GUI 最初需要占用 1 MB 内存,创建全部组件要花 30 秒的时间,则使用这个方法之后仍然需要 1 MB 内存,完全构建它仍然要花 30 秒的时间。LazyPanel 将构建所需 GUI 组件的开销分摊在不同的时间段上,而不是预先就请求全部的内存和时间。但是,LazyPanel 并没有采取任何措施来减少开销。通常,用这个类来加速 GUI 就足够了。但是在另一些情况下,使用一两个 LazyPanel 只是重大性能优化的开始。

小结

通过只进行必需的最小初始化,带有许多组件的 Java GUI 可更快地完成初始化。 LazyPanel 类使您几乎不必进行额外的编程即可加快复杂 GUI 的初始化。对于提高复杂 GUI 的速度而言,这是个不错的起点,对于不太复杂的 GUI,通常只进行这一步就足够了。

作者简介

Mark Roulo 是 JavaWorld 上的 Java 技巧的技术协调员。他从 1989 年开始从事专业编程,并且在 Java 尚处于 alpha-3 版时就开始使用 Java。他是 KLA-Tencor 的全职员工,他参与开发了一种 500KLOC、分布式、并行、多计算机的图像处理(还有其他功能)应用程序,整个程序几乎都是用 Java 编写的。

参考资源

有关 CardLayout 的文档资料:

http://java.sun.com/products/jdk/1.2/docs/api/java/awt/CardLayout.html

关于带选项卡的面板的一篇教程:

http://java.sun.com/docs/books/tutorial/uiswing/components/tabbedpane.html

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有