<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    夢幻之旅

    DEBUG - 天道酬勤

       :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
      671 隨筆 :: 6 文章 :: 256 評論 :: 0 Trackbacks

    Swing的事件處理過程為:事件調度線程(Event Dispatch Thread)從事件隊列(EventQueue)中獲取底層系統捕獲的原生事件,如鼠標、鍵盤、焦點、PAINT事件等。接著調用該事件源組件的dispachEvent。該方法過濾出特殊事件后,調用processEvent進行處理。processEvent方法根據事件類型調用注冊在這個組件上的相應事件處理器函數。事件處理器函數根據這些事件的特征,判斷出用戶的期望行為,然后根據期望行為改變組件的狀態,然后根據需要刷新組件外觀,觸發帶有特定語義的高級事件。此事件繼續傳播下去,直至調用應用程序注冊在該組件上的處理器函數。下圖是這個過程的示意圖:


    上圖所示意的過程簡要說就是:

    Pump an Event->Dispatch & Process Event->MouseListener.mousePressed->fireActionPerformed->ActionListener.actionPeformed->Do database query and display result to a table->Return from actionPerformed->Return from fireActionPerformed->Return from MouseListener.mousePressed->Pump another Event.

    事件調度線程在應用程序事件處理函數actionPerformed沒有完成之前是不能處理下一個事件的,如果應用程序處理函數是一個時間復雜的任務(比如查詢數據庫并將結果顯示到表格中),后面包括PAINT事件將在長時間內得不到執行。由于PAINT事件負責將界面更新,所以這就使用戶界面失去響應。

    打一個比方,事件處理線程就像進入某城唯一的單行道一樣,事件相當于汽車。有種PAINT汽車負責為城市運輸非常重要的生活物資。但是有一天,PAINT前面有一輛汽車突然壞掉了,司機下來修車。但是這車太難修,一修就是幾天,結果后面的PAINT汽車無法前進,物資無法按時運到城里。市民急了,市長雖然不停的打電話催PAINT公司,但即使PAINT公司多添加幾輛車也沒用。由于進城的唯一條路被那輛車給占著,所以再多的PAINT車也只能堵在路上。

    不了解Swing的這種事件處理模型的人往往將時間復雜的任務放在處理函數中完成,這是造成Swing應用程序速度很慢的原因。用戶觸發這個動作,用戶界面就失去了響應,于是給用戶的感覺就是Swing太慢了。其實這個錯誤是程序員造成的,并不是Swing的過失。

    說點題外話,所有采用這種事件模型的用戶界面工具都會產生這種問題,包括SWT、GTK、MFC等流行的用戶界面工具。之所以只有Swing被誤解,主要是和Swing的歷史、市場時機、商業宣傳策略和心理學相關的。

    首先Swing的歷史和市場時機極差。Swing出現早期性能也差、錯誤也多,而Java程序員脫身于傳統圖形界面工具,對于Swing這種新的事件處理模型并不太了解,而此時正處于Java第一輪狂熱的時期,大家都滿懷希望做了大量的Swing應用程序,而這些程序中大量存在這種錯誤方法。于是市場上涌現了大批的這種程序。自從那個時代,因為這些程序,Swing被貼上了慢的標簽。又由于當時的Swing界面也丑,和一般的Windows程序風格炯異,更加深人們的這種印象。這種印象一直持續到現在,像烙印一樣深深的刻在人們的腦海里。

    其次,Swing還有一個致命的問題,就是沒有涌現出一個具有標識性的好程序,這是造成它比SWT印象慘的原因。為什么SWT采用相同的事件處理模型,而獲得了速度快的聲譽呢?這是因為人們當時對于Java做桌面應用的期望心理達到了低谷,而SWT的出現恰恰是伴隨Eclipse出現的,早期的Eclipse的確是在速度快、界面漂亮,這一掃當時人們認為Java慢,Java界面丑陋,Java無法做桌面應用的印象,繼而這個印象被加在SWT身上,人們認為Eclipse速度快、漂亮是因為SWT,其實如果你知道Swing/SWT事件處理模型的話,你就明白功勞是Eclipse開發者的,Eclipse界面漂亮其實要歸功于Eclipse界面設計專家,他們的高水平造就了這個好的IDE,從而也抬起了SWT的聲譽。而Swing的名譽恰恰就被早期Swing低水平開發者給毀了。

    再次, 這和商業宣傳策略有關。IBM和Eclipse很懂得市場宣傳,人們不是認為Java慢嗎,就宣傳SWT使用原生組件,人們不是認為Swing丑陋、風格炯異吧,就宣傳SWT風格一致性,人們不是認為Java不能做桌面應用嗎,就宣傳基于SWT的Eclipse。其實這一切的背后原因只是“人”的不同,Eclipse的開發者和Swing應用程序的開發者,Swing和SWT技術差異并沒有造成那么大的差別,如果是相近能力的人使用他們開發的話,應該能做出相近的產品。這可以從現在Eclipse和NetBeans、Intellij IDEA、JDeveloper和JBuilder看的出來。

    最后,人類有一個心理學現象,就是一旦形成對某種事物的印象,很難擺脫舊的認識,有時甚至人們不愿意承認擺在眼前的事實。總而言之,Swing和SWT不同遭遇是因為歷史、市場時機、商業宣傳策略、心理學的種種原因造成的。

    那么如何避免這個問題,編寫響應速度快的Swing應用程序呢?在SwingWorker的javadoc中有這樣兩條原則:

    Time-consuming tasks should not be run on the Event Dispatch Thread. Otherwise the application becomes unresponsive. 耗時任務不要放到事件調度線程上執行,否則程序就會失去響應。

    Swing components should be accessed on the Event Dispatch Thread only. Swing組件只能在事件調度線程上訪問。

    因此處理耗時任務時,首先要啟動一個專門線程,將當前任務交給這個線程處理,而當前處理函數立即返回,繼續處理后面未決的事件。這就像前面塞車的例子似的,那個司機只要簡單的把車開到路邊或者人行道上修理,整個公路系統就會恢復運轉。

    其次,在為耗時任務啟動的線程訪問Swing組件時,要使用SwingUtilties. invokeLater或者SwingUtilities.invokeAndWait來訪問,invokeLater和invokeAndWait的參數都是一個Runnable對象,這個Runnable對象將被像普通事件處理函數一樣在事件調度線程上執行。這兩個函數的區別是,invokeLater不阻塞當前任務線程,invokeAndWait阻塞當前線程,直到Runnable對象被執行返回才繼續。在前面塞車的例子中,司機在路邊修車解決了塞車問題,但是他突然想起來要家里辦些事情,這時他就可以打個電話讓家里開車來。假如修車不受這件事情的影響,比如叫家人送他朋友一本書,他可以繼續修車,這時就相當于invokeLater;假如修車受影響,比如缺少某個汽車零件,叫家人給他送過來,那么在家人來之前,他就沒法繼續修車,這時就相當于invokeAndWait。

    下面舉一個例子說明這兩點,比如按下查詢按鈕,查詢數據量很大的數據庫,并顯示在一個表中,這個過程需要給用戶一個進度提示,并且能動態顯示表格數據動態增加的過程。假設按鈕的處理函數是myButton_actionPerformed,則:

    void myButton_actionPerformed(ActionEvent evt){
    new MyQueryTask().start();
    }
    public class MyQueryTask extends Thread{
    public void run(){
    //查詢數據庫
    final ResultSet result=...;
    / /顯示記錄
    for(;result.next();){
    //往表的Model中添加一行數據,并更新進度條,注意這都是訪問組件
    SwingUtilities.invokeLater(new Runnable(){
    public void run(){
    addRecord(result);
    }
    });
    }
    ....
    }
    void addRecord(ResultSet result){
    //往表格中添加數據
    jTable.add....
    //更新進度條
    jProgress.setValue(....);
    }
    }

    JDK1.6以后,Swing提供了一個專門的類SwingWorker能幫你解決這個編程范式,你所需要做的就是繼承這個類,重載doInBackground,然后在actionPeformed中調用它的execute方法,并通過publish/process方法來更新界面。SwingWorker的主要方法和它們的作用在下面的示意圖:

     


    從上面示意圖可以看出,SwingWorker實際上不過是封裝了前面我所說的例子中的MyQueryTask,并做了更詳盡的考慮。execute方法相當于MyQueryTask線程start,它啟動這個后臺線程并立刻返回。SwingWorker可以注冊PropertyChangeListener,這些listener都被在事件調度線程上執行,相當于MyQueryTask中的那些訪問組件的Runnable對象。另外,publish、setProgress只不過是特殊的property事件吧,process和done不過是響應publish和PropertyChangeEvent.DONE這個事件的方法罷了。因此我們很容易將上面的例子改成SwingWorker的版本:

    void myButton_actionPerformed(ActionEvent evt){
    new MyQueryTask().execute();
    }

    public class MyQueryTask extends SwingWorker{
    public void doInBackground(){
    //查詢數據庫
    final ResultSet result=...;
    //顯示記錄
    for(;result.next();){
    //往表的Model中添加一行數據,并更新進度條,注意這都是訪問組件
    publish(result);
    }
    ....
    }
    public void process(Object ... result){
    //往表格中添加數據
    jTable.add....
    //更新進度條
    jProgress.setValue(....);
    }
    }

    對于一般的耗時任務這樣做是比較普遍的,但是有一些任務是一旦觸發之后,會周期性的觸發,如何做處理這種任務呢?JDK中提供了兩個Timer類幫你完成定時任務,一個是javax.swing.Timer,一個java.util.Timer。使用它們的方法很簡單,對于Swing的timer,使用方法如下:

    public void myActionPerformed(){
    //假設點擊了某個按鈕開始記時
    Action myAction=new AbstractAction(){
    public void actionPerformed(ActionEvent e){
    //做周期性的活動,比如顯示當前時間
    Date date=new Date();
    jMyDate.setDate(date);//jMyDate是個假想的組件,能顯示日期時間
    }
    };
    new Timer(1000, myAction).start();
    }

    java.util.Timer類似,只不過使用TimerTask完成動作封裝。注意這兩個Timer有一個關鍵的區別:Swing的Timer的事件處理都是在事件調度線程上進行的,因而它里面的操作可以直接訪問Swing組件。而java.util.Timer則可能在其他線程上,因而訪問組件時要使用SwingUtilities.invokeLater和invokeAndWait來進行。這一點要記住。

    如果要了解更詳細的信息,可以查閱SwingWorker、Swing Timer和util Timer這些類javadoc文檔和其他網上資料。最重要的是要記住了那兩條原則。

    轉自:WilliamChen

    posted on 2012-12-10 17:40 HUIKK 閱讀(928) 評論(0)  編輯  收藏

    只有注冊用戶登錄后才能發表評論。


    網站導航:
     
    主站蜘蛛池模板: 亚洲av女电影网| 成年免费a级毛片免费看无码| 亚洲乱亚洲乱妇无码麻豆| 久久久高清免费视频| 国产精品1024在线永久免费| 久久综合久久综合亚洲| 亚洲最大的成网4438| 久久亚洲AV无码西西人体| 国产大片免费观看中文字幕| 可以免费看黄的网站| 亚欧免费一级毛片| a毛片在线免费观看| 污网站免费在线观看| 亚洲AV无码一区二区三区牲色| 亚洲国产福利精品一区二区| 久久精品国产亚洲av麻| 亚洲国产一区二区视频网站| 四虎在线播放免费永久视频 | 国产成人高清精品免费鸭子| 国产大片91精品免费观看不卡| 久久久久久久久久国产精品免费| www免费黄色网| 人成电影网在线观看免费| 黄色a三级三级三级免费看| 亚洲日韩AV无码一区二区三区人| 2019亚洲午夜无码天堂| 亚洲最大在线视频| 亚洲精品在线免费观看| 久久久久久亚洲Av无码精品专口| 水蜜桃亚洲一二三四在线| 久久精品国产69国产精品亚洲| 亚洲综合无码AV一区二区| 中文亚洲AV片在线观看不卡| 亚洲免费日韩无码系列 | 亚洲成人福利在线| 亚洲日本精品一区二区| 亚洲精品国产成人99久久| 亚洲天堂中文资源| 亚洲免费在线视频播放| 亚洲av永久无码嘿嘿嘿| 亚洲人成色4444在线观看|