2016-07-22 21 views
3

我正在設計一個API,使大量使用異步代碼完成塊。設計模式回調任意在UI線程

public interface IResult<T extends Result.I> { 
    void onResult(T); 
} 

public void doXYZ(IResult<Result.TypeA> iResult) { ... } 

用戶會打電話給我的API的doXYZ(),和一個單獨的網絡工作者線程後來稱之爲iResult.onResult()。有時他並不關心onResult()被調用哪個線程,有時候他可能只想更新UI - 只是尋找一種方法來隱式卸載用戶的負擔,以便他們可以保持匿名內部類的整潔,小。

這個API適用於Android,在很多(但不是全部)時間,用戶可能想要在影響UI元素的onResult()回調中做些事情,因此必須在UI線程上運行它們的代碼。而不是讓用戶不得不用runOnUIThread(...)調用來弄髒他們的代碼,我想知道是否人們知道設計模式可以使doXYZ()調用時更容易用戶指定是否在其UI線程上調用回調。

  1. 出於性能方面的原因,我寧願不強求,我的API始終調用onResult()在UI線程上。我見過

  2. 一種模式是添加一個方法參數:doXYZ(...,布爾callOnUIThread)。但是,由於API是有文檔記錄的,所以我寧願不要弄髒所有這些額外的布爾值文檔(和方法簽名)。

  3. 另一個想法是重載每個doXYZ(),使其中一個沒有callOnUIThread參數,但對於文檔而言,這對於膨脹是非常不利的。

  4. 另一個以爲我是一個onResultUI()方法添加到IResult,除了onResult(),但是,通過讓他們不得不總是在他們的匿名類定義都弄髒了用戶的代碼。

任何建議乾淨的方式來完成這個: i。簡明 ii。非常適合代碼可讀性 iii。容易在文檔上 ?

+0

想想用途代碼:你知道,如果代碼需要在GUI線程上運行創造了'IResult'實例的意義呢?在這種情況下,你可以提供一個標記接口'IRunsOnGui',如果客戶希望'onResult'在gui-thread上運行,那麼客戶端可以擴展它。當你添加'IResults'時,檢查它是否是'IRunsOnGui'的一個實例,並且只允許結果在gui-thread上運行。你可以看到這個例子[在ideone上](http://ideone.com/whEDB2)。如果這回答你的問題,我很樂意把它放在答案。 – BeyelerStudios

+0

因此,基本上'doXYZ'正在調度將通過接口報告結果的異步作業。在這種情況下,標記和裝飾器都非常適合(甚至可以像我在[ideone-link](http://ideone.com/whEDB2)中看到的那樣組合):在示例中,Foreman bob將執行調度,'bob.dirve '會是你的'doXYZ')。選擇你喜歡的任何一種:通過裝飾他的'IResult'或者標記它以不同的方式運用給被調控者。在'doXYZ'中執行檢查是否派遣到您的一般工作者池或gui線程。 – BeyelerStudios

回答

2

使用爲IResult一個裝飾圖案。

E.g.如果你有一個接口IResult(我只是做了更方便演示泛型類型T)

interface IResult<T> { 
    void onResult(T result); 
} 

和結果的回調實現這樣

class PrintCurrentThreadAndResult implements IResult<String> { 
    @Override 
    public void onResult(String result) { 
    Thread thread = Thread.currentThread(); 
    System.out.print(thread.getName()); 
    System.out.print(" - "); 
    System.out.println(result); 
    } 
} 

您可以創建確保裝修裝飾器結果將在特殊線程(例如ui線程)上調用。我在這裏使用swing的事件調度程序線程,以便每個人都可以輕鬆地編譯和測試它。

class UIThreadAwareResult<T> implements IResult<T> { 

    private IResult<T> delegate; 

    public UIThreadAwareResult(IResult<T> delegate) { 
    this.delegate = delegate; 
    } 

    @Override 
    public void onResult(T result) { 
    invokeOnUIThread(result); 
    } 

    private void invokeOnUIThread(final T result) { 
    try { 
     EventQueue.invokeAndWait(new Runnable() { 
     @Override 
     public void run() { 
      delegate.onResult(result); 
     } 
     }); 
    } catch (Exception e) { 
     throw new RuntimeException(e); 
    } 
    } 
} 

然後客戶端代碼可以通過裝飾原始結果來選擇要運行的線程。

public class Main { 

    public static void main(String[] args) { 
    Main main = new Main(); 

    IResult<String> result = new PrintCurrentThreadAndResult(); 
    main.doXYZ(result); 

    IResult<String> uiThreadAwareResult = new UIThreadAwareResult<String>(result); 
    main.doXYZ(uiThreadAwareResult); 
    } 

    public void doXYZ(IResult<String> iResult) { 
    iResult.onResult("Hello"); 
    } 
} 

輸出將是

main - Hello 
AWT-EventQueue-0 - Hello 
+1

@BeyelerStudios因爲我不知道OP的'doXYZ'方法是如何實現的我使用'invokeAndWait'。可能是'doXYZ'方法首先計算一些結果,然後將它傳遞給'IResult',最後再做一些必須與'onResult'方法同步的東西。 –

+0

@BeyelerStudios你甚至在'工作者線程中運行'是什麼意思? –

+0

我讀了OP的一個API,它使得大量使用異步代碼完成塊*成爲:他要麼運行異步計算並提供如何處理結果的接口('onResult'),要麼他以某種方式異步調度onResult。你的裝飾器在兩種情況下都會在工作線程上運行'invokeAndWait',所以代碼完成並不是異步的。 – BeyelerStudios