2016-03-01 13 views
0

Guice 4.0在這裏。我已經使用Guice幾年了,真的很喜歡它。我現在需要一個Swing應用程序,並且對如何設計Guice API來滿足Swing(恕我直言)怪異的需求/最佳實踐感到困惑。Guice和Swing的併發模型看起來不兼容

通常,當我使用吉斯,我有一個主要的「驅動器」類像MyApp我絲吉斯像這樣:

// Groovy pseudo-code 
class MyApp { 
    @Inject 
    FizzClient fizzClient 

    @Inject 
    BuzzClien buzzClient 

    static void main(String[] args) { 
     MyApp myapp = Guice.createInjector(new MyAppModule()).getInstance(MyApp) 
     myapp.run() 
    } 

    private void run() { 
     // Do whatever the app does... 
    } 
} 

class MyAppModule extends AbstractModule { 
    @Override 
    void configure() { 
     bind(FizzClient).to(DefaultFizzClient) 
     bind(BuzzClient).to(DefaultBuzzClient) 
     // etc... 
    } 
} 

但在擺動的情況下,JFrame的GUI應用程序,所以對我來說,是有意義的有MyApp延長JFrame

class MyApp extends JFrame { 
    // etc... 
} 

的問題是,在搖擺,你需要做一些非常規的線程掛羊頭賣狗肉,使:

  1. 操縱的JFrame或的其他JComponents的任何代碼的UI需要在事件調度線程(EDT)發生 - 這是通過完成SwingUtilities.invokeLater;和
  2. 任何代碼,將阻塞並需要一段時間才能完成需要被轉移到了自己的SwingWorker螺紋

所以我們可以說我有一個需要熱身在應用程序啓動大緩存(或許這需要30-60秒)。這是我最好的嘗試迄今:

@Canonical // Creates a tuple constructor and a bunch of other stuff for me 
@Slf4j 
class MyApp extends JFrame { 
    @Inject 
    MyAppCache appCache 

    static void main(String[] args) { 
     SwingUtilities.invokeLater(new Runnable() { 
      @Override 
      void run() { 
       // The Guice-based bootstrapping has to happen inside invokeLater 
       // because MyApp *is* a JFrame (hence UI). 
       Guice.createInjector(new MyAppModule()).getInsance(MyApp).init() 
      } 
     }) 
    } 

    void init() { 
     new SwingWorker<Void,String>() { 
      @Override 
      protected Void doInBackground() { 
       appCache.warmUp() 
       publish('Cached is warmed up!') 
      } 

      @Override 
      protected void process(List<String> updates) { 
       log.info(updates) 
      } 
     }.execute() 
    } 
} 

class MyAppModule extends AbstractModule { 
    @Override 
    void configure() { 
     bind(MyAppCache).to(DefaultAppCache) 
    } 

    @Provides 
    MyApp providesMyApp(MyAppCache appCache) { 
     MyApp app = new MyApp(appCache) 
     app.title = 'My App' 
     app.defaultCloseOperation = JFrame.EXIT_ON_CLOSE 
     app.pack() 
     // ...etc. (configure the parent JFrame) 
    } 
} 

當我運行這段代碼我上SwingWorkerappCache.warmUp()語句NPE。這是因爲上面的代碼實際上涉及3個主題:

  • 主要 - 在main & init運行
  • EDT - 在吉斯是自舉MyApp實例
  • 搖擺工人 - 哪裏SwingWorker正在運行並試圖預熱高速緩存

所以當我們去執行Swing worker時,Guice還沒有機會引導MyApp實例(因此它的緩存屬性)。

Ay的想法(w /特定代碼示例)我如何讓Guice能夠與Swing的併發模型正常工作?

+1

你在傳遞給'invokeLater'的Runnable中調用'init'。爲什麼它會在主線程而不是EDT線程上運行? –

+1

我真的很想學習一個不是單線程的GUI庫。 – trashgod

+0

謝謝@JeffBowman(+1) - 因爲'SwingWorker'有一些神奇的功能 - 你自己運行這個代碼,你會看到工作代碼在自己的線程中執行,與主和EDT分開。 – smeeb

回答

2

我對不熟悉,但您可以在調用EventQueue.invokeLater()後繼續任何非GUI初始化 - 這些代碼只會在initial thread上運行。可以看到一個例子here。當然,您必須同步對任何共享數據的訪問,但是當初始線程以通常方式使用EventQueue.invokeLater()結束時,您可以更新GUI。您的圖形用戶界面應該暫時禁止使用任何不完整的數據。我不認爲MyApp延伸JFrame。相反,add()您將Container附加到JFrame的實例。引用了幾個例子here

+0

Thanks @trashgod(+1) - but * believe * me,I've tried everything,even make'MyApp' * not * extend'JFrame'。 **但是,下面的問題的答案可能會給我一個解決方案:**所以Guice bootstrapping必須發生在其中一個線程:main,EDT或Swing Worker(「* Cache Warmer *」)線程中。老實說,我不在乎它是哪一個。但是,在Cache Warmer Thread嘗試預熱緩存之前,我需要* Guice引導並連接'MyApp'實例。 – smeeb

+0

或許我所需要做的就是做一些同步,以確保Cache Warmer線程等待Guice注入完成。任何想法我可能會做到這一點(即使沒有Guice的經驗,所有的代碼都在上面提供)? – smeeb

+0

不知道我可以提供萬能藥;按照[這裏](http://stackoverflow.com/a/11372932/230513)的建議,你可以使用'CountDownLatch'來同步非GUI線程;還要小心[繁忙的循環](http://stackoverflow.com/q/35154352/230513)。 – trashgod