5. 延遲重畫操作
對於圖形用戶界面的應用來說,性能低下的主要原因往往可以歸結爲重畫屏幕的效率低下。當用戶改變窗口大小或者滾動一個窗口時,這一點通常可以很明顯地觀察到。改變窗口大小或者滾動屏幕之類的操作導致重畫屏幕事件大量地、快速地生成,甚至超過了相關代碼的執行速度。對付這個問題最好的辦法是忽略所有“遲到”的事件。
建議在這裏引入一個數毫秒的時差,即如果我們立即接收到了另一個重畫事件,可以停止處理當前事件轉而處理最後一個收到的重畫事件;否則,我們繼續進行當前的重畫過程。
如果事件要啓動一項耗時的工作,分離出一個工作線程是一種較好的處理方式;否則,一些部件可能被“凍結”,因爲每次只能處理一個事件。下面提供了一個事件處理的簡單例子,但經過擴展後它可以用來控制工作線程。
public static void runOnce(String id, final long milliseconds) { synchronized(e_queue) { // e_queue: 所有事件的集合 if (!e_queue.containsKey(id)) { e_queue.put(token, new LastOne()); } } final LastOne lastOne = (LastOne) e_queue.get(token); final long time = System.currentTimeMillis(); // 獲得當前時間 lastOne.time = time; (new Thread() {public void run() { if (milliseconds > 0) { try {Thread.sleep(milliseconds);} // 暫停線程 catch (Exception ex) {} } synchronized(lastOne.running) { // 等待上一事件結束 if (lastOne.time != time) // 只處理最後一個事件 return; } }}).start(); } private static Hashtable e_queue = new Hashtable(); private static class LastOne { public long time=0; public Object running = new Object(); } |
6. 使用雙緩衝區
在屏幕之外的緩衝區繪圖,完成後立即把整個圖形顯示出來。由於有兩個緩衝區,所以程序可以來回切換。這樣,我們可以用一個低優先級的線程負責畫圖,使得程序能夠利用空閒的CPU時間執行其他任務。下面的僞代碼片斷示範了這種技術。
Graphics myGraphics; Image myOffscreenImage = createImage(size().width, size().height); Graphics offscreenGraphics = myOffscreenImage.getGraphics(); offscreenGraphics.drawImage(img, 50, 50, this); myGraphics.drawImage(myOffscreenImage, 0, 0, this); |
7. 使用BufferedImage
Java JDK 1.2使用了一個軟顯示設備,使得文本在不同的平臺上看起來相似。爲實現這個功能,Java必須直接處理構成文字的像素。由於這種技術要在內存中大量地進行位複製操作,早期的JDK在使用這種技術時性能不佳。爲解決這個問題而提出的Java標準實現了一種新的圖形類型,即BufferedImage。BufferedImage子類描述的圖形帶有一個可訪問的圖形數據緩衝區。一個BufferedImage包含一個ColorModel和一組光柵圖形數據。這個類一般使用RGB(紅、綠、藍)顏色模型,但也可以處理灰度級圖形。它的構造函數很簡單,如下所示:
public BufferedImage (int width, int height, int imageType) |
ImageType允許我們指定要緩衝的是什麼類型的圖形,比如5-位RGB、8-位RGB、灰度級等。
8. 使用VolatileImage
許多硬件平臺和它們的操作系統都提供基本的硬件加速支持。例如,硬件加速一般提供矩形填充功能,和利用CPU完成同一任務相比,硬件加速的效率更高。由於硬件加速分離了一部分工作,允許多個工作流併發進行,從而緩解了對CPU和系統總線的壓力,使得應用能夠運行得更快。利用VolatileImage可以創建硬件加速的圖形以及管理圖形的內容。由於它直接利用低層平臺的能力,性能的改善程度主要取決於系統使用的圖形適配器。VolatileImage的內容隨時可能丟失,也即它是“不穩定的(volatile)”。因此,在使用圖形之前,最好檢查一下它的內容是否丟失。VolatileImage有兩個能夠檢查內容是否丟失的方法:
public abstract int validate(GraphicsConfiguration gc);public abstract Boolean contentsLost(); |
每次從VolatileImage對象複製內容或者寫入VolatileImage時,應該調用validate()方法。contentsLost()方法告訴我們,自從最後一次validate()調用之後,圖形的內容是否丟失。雖然VolatileImage是一個抽象類,但不要從它這裏派生子類。VolatileImage應該通過Component.createVolatileImage()或者GraphicsConfiguration.createCompatibleVolatileImage()方法創建。
9. 使用Window Blitting
進行滾動操作時,所有可見的內容一般都要重畫,從而導致大量不必要的重畫工作。許多操作系統的圖形子系統,包括WIN32 GDI、MacOS和X/Windows,都支持Window Blitting技術。Window Blitting技術直接在屏幕緩衝區中把圖形移到新的位置,只重畫新出現的區域。要在Swing應用中使用Window Blitting技術,設置方法如下:
setScrollMode(int mode); |
在大多數應用中,使用這種技術能夠提高滾動速度。只有在一種情形下,Window Blitting會導致性能降低,即應用在後臺進行滾動操作。如果是用戶在滾動一個應用,那麼它總是在前臺,無需擔心任何負面影響。(T117)