1. 程式人生 > >Swing, RAP(RCP), Android 程式開發中,GUI重新整理實現方式對比

Swing, RAP(RCP), Android 程式開發中,GUI重新整理實現方式對比

今天我們說一說基於Java語言的幾種GUI程式開發中,GUI重新整理實現方式的對比。

Swing,RAP(RCP也是一樣),Android其介面重新整理的原理都是一樣的:介面重新整理只有一個執行緒,我們稱其為UIThread,所有重新整理介面的操作(如更新進度條上的進度)都必須通過這個執行緒來操作,否則若通過外部執行緒直接操作的話,會導致資料不一致。
由於整個介面都由UITread負責更新,因此,UIThread只能用來執行更新介面的操作,其他的耗時的操作是不允許的,如連線網路伺服器。否則就會出現介面無響應的現象。


我曾經想過為什麼不能允許多個執行緒去重新整理介面?
我想(這個是我個人想法歡迎拍磚),有兩點原因:
1)介面的行為本身是單執行緒的,使用者只能一次做一個操作:使用者不可以一次在兩個輸入框裡面輸入文字。
2)由於第一點,因此多執行緒重新整理介面帶來不了任何好處,且這麼做會程式碼與介面相關的操作都需要同步(synchronized)或使用佇列,者會使系統變的更復雜,除此之外沒有任何好處。


(當然,MFC介面開發與這個原理不一樣)。

  • 我們先說Swing。
  • Swing曾經是壟斷了Java語言的介面開發,當時幾乎所有的跨平臺的介面程式都使用Swing開發(典型的有Oracle客戶度,DB2客戶端等)。當然現在他被RCP(稱為SWT更精確)取代了,但是我們還是有必要了解。
    Swing開發者都知道Swing中的UIThread被稱為事件派發執行緒。一個GUI Client只有一個UIThread,由於Swing是C/S結構的,因此會簡單一些。

    我們要在Swing中更新介面,必須將更新介面的操作放到事件派發執行緒中:

    SwingUtilities.invokeLater(runnable);
    SwingUtilities.invokeAndWait(runnable);

    if(SwingUtilities.isEventDispatchThread())
    {
    //just do it
    }
    else
    {
    //SwingUtilities.invokeLater(runnable);
    }

    invokeLater是不阻塞當前介面執行緒,放到事件派發執行緒的佇列中去執行。實際上這個執行的延遲不會超過5秒,除非有使用者互動的介面在等待使用者反饋(如彈出框等待使用者確認),否則介面自身就有問題了。
    invokeAndWait是等待介面執行緒執行,然後當前執行緒才返回。
    他們都需要接受一個Runnable物件的引數。
    我們還可以精細化處理,判斷當前執行緒是否是事件派發執行緒,是就直接執行。

    在Swing介面中,如果點選某個按鈕或選單後,後臺的響應需要耗時較長(如通過網路查詢資料回來),則你需要使用後臺執行緒來執行,並將事件派發執行緒suspend,並將介面置為忙等(滑鼠漏斗形狀)狀態,等響應回來後,使用SwingUtilities.invokeLater方法將資料重新整理到介面上。

  • 再說說RAP
  • RAP是基於RCP的基礎API,加上了一套Java程式碼轉換為javascript程式碼的機制而形成的。準確說不是程式碼之間的轉換,是RAP框架中定義了一套Widget,這套Widget的定義和行為可以用Java程式碼來開發(開發方法與RCP程式一模一樣),然後RAP框架在執行時會將RCP的Widget轉換為javascript的,然後在Web上執行。詳細可以參考官方網站

    RAP中重新整理介面,與Swing是類似的,也是一個Client只有一個UITrhead,RAP中就是稱呼為:UITrehad。
    我們開發這寫RAP程式碼重新整理介面的時候,實際上是很簡單的:

    Display display = getDisplay();
    display.syncExec(runnable);
    display.asyncExec(runnable);

    每個client有唯一一個Display物件,display.syncExec(runnable)是立即重新整理介面,display.asyncExec(runnable)是將重新整理事件放到UIThread的佇列中排隊重新整理。分別對應SwingUtilities.invokeAndWait(runnable)和SwingUtilities.invokeLater(runnable)方法。

    但是,作為開發者還是要了解其中的差異,否則開發的程式碼有bug了自己都定位不出來。

    RAP是Web程式,本質上是javascript的。但是,與一般的Web程式還不一樣,RAP的繪製介面的機制是:

    1)RAP使用了Qooxdoo作為JS框架;
    2)在應用程式加在的時候,會將Qooxdoo和RAP這兩部分框架程式碼加在到瀏覽器;
    3)瀏覽器中的RAP框架請求sever繪製應用介面(如View,Tree等);
    4)server根據client傳送過來的請求,返回對應的繪製介面的JS程式碼給client;--這裡是UIThread處理的。
    5)client執行server傳送過來的程式碼,繪製介面。
    6)後續新開啟的介面,都是通過client請求server,server傳送JS程式碼到瀏覽器執行生成介面。

    這裡我們發現UIThread是處理client響應生成JS程式碼的,但是我們要知道,這個是RAP框架做的事情。
    對於我們使用RAP作為框架來開發的開發者,實際上執行到的就是構造我們的Composite或重新整理介面的方法。
    為了更進一步的闡述,我們看下面的圖:

    圖片中地下方框S字母是一個RAP Server,通常執行在OSGi中。大的S方框中的每個小I方框是一個client例項(裡面就包含一個UIThread),長方形的C方框是所有client共享的資料(如SessionStore等)。上面圓圈是client。
    這個結構我們可以看到與Swing是完全不一樣的,RAP在Server端有多例項,多個UITrhead,還有共享的資料,比Swing要複雜很多。

    因此,我們可以想的到,UIThread s 與共享資料之間,必定存在比較複雜的鎖同步機制。
    所以,我們在使用RAP開發的時候要特別注意,我們放到UIThread中執行的程式碼,要儘量少加鎖,一定要加鎖的話要小範圍,並明確不會與RAP自身的鎖造成死鎖。否則很容易出現介面死鎖的問題。

    從RAP 的bug庫上來看,UIThread死鎖的問題是最難解決的。

  • 最後說說Android
  • 從RAP複雜的同步鎖裡面出來後,你會覺得Android簡單很多。
    從結構上來說,Android與Swing是一致的,C/S結構。

    Android的UIThread更新方法是:

    Activity.runOnUiThread(Runnable)

    這個方法也是一樣,不允許做耗時的操作,否則介面會無響應。

    若一定要做耗時的操作,如點登入按鈕,要連線到後臺登入。那麼推薦的方法是AsyncTask(當然你也可以自己些後臺執行緒,但就走彎路了)。
    我們從一個例子來闡述AsyncTask的基本用法。

    //我們繼承了AsyncTask,AsyncTask的模板引數定義的有點彆扭。
    //第一個引數表示doInBackground函式的輸入引數型別,這裡是UserLoginReqJSON
    //第二個引數表示進度
    //第三個引數表示doInBackground返回值型別,且onPostExecute的輸入引數
    private class LoginTask extends AsyncTask{

    //這個函式是執行耗時的後臺操作,這裡是登入後臺
    @Override
    protected HandlerResultJSONHolder doInBackground(UserLoginReqJSON... arg0) {
    //user the LoginProc object to perform the login action
    HandlerResultJSONHolder resp_holder = new HandlerResultJSONHolder();
    LoginProc login_proc = UserJSONInvoker.getLoginProc(arg0[1], resp_holder);
    login_proc.run();
    return resp_holder;
    }

    //這個函式是重新整理介面,不能做耗時的操作
    @Override
    protected void onPostExecute(HandlerResultJSONHolder resp_holder){
    if (resp_holder.getHandlerResultJSON().getErrorCode() == 0){
    Intent i = new Intent(getApplicationContext(), MainActivity.class);
    LoginActivity.this.startActivity(i);
    }else{

    }
    }

最後彙總比對,Swing和Android結構上相對RAP來說要簡單,開發框架上Android更易用,畢竟Android是後出來的,符合程式設計的進化規則。RAP在結構上相對複雜,不容易把握,要些處穩定的程式,需要了解其原理,不要被其開發框架騙了。