2011-09-22 59 views
3

我創建了一個簡單的Java應用程序,每秒鐘連續10秒鐘向JTable添加一個新行。它由三個類組成。JAR Bundler使用OSXAdapter導致應用程序滯後或終止

一旦程序啓動

public class JarBundlerProblem { 
    public static void main(String[] args) 
    { 
     System.err.println("Initializing controller"); 
     new Controller(); 
    } 
} 

創建GUI和改變它通過doWork()

public class Controller { 
    public Controller() 
    { 
     doWork(null); 
    } 
    public static void doWork(String s) 
    { 
     GUI gui = new GUI(); 

     for (int i=0; i<10; i++) 
     { 
      gui.addRow("Line "+(i+1)); 
      try { 
       Thread.sleep(1000); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     } 
    } 
} 

最後,GUI控制器,該控制器被稱爲主類

import javax.swing.JFrame; 
import javax.swing.JScrollPane; 
import javax.swing.JTable; 
import javax.swing.table.DefaultTableModel; 

public class GUI { 
    private JFrame frame = new JFrame(); 
    private DefaultTableModel model = new DefaultTableModel(); 
    private JTable table = new JTable(model); 
    private JScrollPane pane = new JScrollPane(table); 

    public GUI() 
    { 
     model.addColumn("Name"); 

     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     frame.add(pane); 
     frame.pack(); 
     frame.setVisible(true); 
    } 
    public void addRow(String name) 
    { 
     model.addRow(new Object[]{name}); 
    } 
} 

由於我開發OS X的,我需要能夠到我的應用程序與特定文件類型關聯(比方說.jarbundlerproblem),我一直在使用Apple Jar Bundler捆綁我JAR文件到APP。我已經成功完成了這項工作,我的應用程序打開,數到十,每秒寫出。現在

,對這個問題

默認情況下,雙擊.jarbundlerproblem,並且該文件與我的應用程序相關聯,將無法通過我雙擊作爲參數傳遞給應用程序的文件。顯然,這只是OS X上的Java作品。

由於我需要能夠看到雙擊的文件,我正在使用OSXAdapter這是Apple爲此目的製作的Java庫。這一點,我已經通過改變我Controller類的構造函數來實現,加上另一個方法registerForMacOSXEvents()

public Controller() 
{ 
    registerForMacOSXEvents(); 
    //doWork(null); 
} 
public void registerForMacOSXEvents() { 
    try { 
     OSXAdapter.setFileHandler(this, getClass().getDeclaredMethod("doWork", new Class[] { String.class })); 
    } catch (Exception e) { 
     System.err.println("Error while loading the OSXAdapter:"); 
     e.printStackTrace(); 
    } 
} 

但這(未成年人)的修改之後,我的應用程序開始演戲了。有時,即使在控制檯中可以看到它剛剛啓動(即寫入了Initializing controller),但它仍然不會打開,但經過幾次嘗試後,它最終會啓動,但前10秒窗口將完全空白,之後,將添加10行。

幫助

現在,我已經竭盡全力來應付這一相當多,而且好像還沒有關於既不OSXAdapter也不罐捆紮機大量的文檔資料。我究竟做錯了什麼?或者我不應該在第一時間使用OSXAdapter或Jar Bundler?

+0

只是爲了確保我瞭解OSXAdapter(不熟悉MAC):基本上這是一個線程運行 - 你的控制範圍之外 - 這與字符串短信中的doWork報告其結果,直到它準備好(文件名?) ,註冊後立即開始?它如何表示準備好了? – kleopatra

+0

很高興幫助:-)請你出示你的解決方案(沒有掛單)? – kleopatra

+0

@kleopatra我不知道OSXAdapter是如何工作的,我只是簡單地熟悉它。如果您有興趣瞭解更多信息,請訪問http://developer.apple.com/library/mac/#samplecode/OSXAdapter/Listings/src_OSXAdapter_java。HTML#// apple_ref/DOC/UID/DTS10000685-src_OSXAdapter_java-DontLinkElementID_5 – kba

回答

4

這件事以後,我並不完全相信一個SwingWorker的是一個簡單的(又名:更好的)解決方案 - 仍需要額外的線程synching(工作線程和「外」螺紋之間通過文件/名稱)。反正(抽空學習,並通過錯誤:)是它的機會,下面是概念例子的基本思想粗證明:

  • 實現控制器的SwingWorker,其中漏斗輸入從外螺紋成在EDT
  • 使其接受通過方法的doWork(..)哪個隊列輸入用於發佈
  • 實現doInBackground到succesively發佈輸入

開放問題

輸入(從適配器,網絡)
  • 同步接入到本地列表(沒有併發的專家,但可以肯定的是需要做的)
  • 可靠地檢測外螺紋的端部(這裏簡單地停止在輸入隊列爲空)

反饋歡迎:-)

public class GUI { 
    private JFrame frame = new JFrame(); 
    private DefaultTableModel model = new DefaultTableModel(); 
    private JTable table = new JTable(model); 
    private JScrollPane pane = new JScrollPane(table); 

    public GUI() { 
     model.addColumn("Name"); 

     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     frame.add(pane); 
     frame.pack(); 
     frame.setVisible(true); 
    } 

    public void addRow(String name) { 
     model.addRow(new Object[] { name }); 
    } 

    /** 
    * Controller is a SwingWorker. 
    */ 
    public static class Controller extends SwingWorker<Void, String> { 
     private GUI gui; 

     private List<String> pending; 

     public Controller() { 
      gui = new GUI(); 
     } 

     public void doWork(String newLine) { 
      if (pending == null) { 
       pending = new ArrayList<String>(); 
       pending.add(newLine); 
       execute(); 
      } else { 
       pending.add(newLine); 
      } 
     } 

     @Override 
     protected Void doInBackground() throws Exception { 
      while (pending.size() > 0) { 
       publish(pending.remove(0)); 
       try { 
        Thread.sleep(1000); 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 
      } 
      return null; 
     } 

     /** 
     * @inherited <p> 
     */ 
     @Override 
     protected void process(List<String> chunks) { 
      for (String object : chunks) { 
       gui.addRow(object); 
      } 
     } 

    } 

    /** 
    * Simulating the adapter. 
    * 
    * Obviously, the real-thingy wouldn't have a reference 
    * to the controller, but message the doWork refectively 
    */ 
    public static class Adapter implements Runnable { 

     Controller controller; 

     public Adapter(Controller controller) { 
      this.controller = controller; 
     } 

     @Override 
     public void run() { 
      for (int i=0; i<10; i++) 
      { 
       controller.doWork("Line "+(i+1)); 
       try { 
        Thread.sleep(100); 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 
      } 
     } 

    } 
    public static void main(String[] args) 
    { 
     System.err.println("Initializing controller"); 
     new Adapter(new Controller()).run(); 
    } 

    @SuppressWarnings("unused") 
    private static final Logger LOG = Logger.getLogger(GUI.class.getName()); 
} 
+0

+1重新開放的問題:在'doWork()'上使用'synchronized'應該足夠了,'SwingWorker'的'get()'方法將一直等到它的後臺線程退出。我(http://stackoverflow.com/questions/7519244/jar-bundler-using-osxadapter-causing-application-to-lag-or-terminate/7568947#7568947)附近連續添加[變化]。 – trashgod

+0

謝謝,kleopatra,使用你的例子,我終於設法得到它所有的工作。我做了,但分開了類,並將所有工作都移到了'doInBackground'中,並一起擺脫了「待處理」列表。 – kba

+0

我同意'SwingWorker'是更好的選擇;這聽起來好像不需要等候隊列。 – trashgod

6

它看起來像你阻止event dispatch thread(美國東部時間)。 SwingWorker將是更好的選擇,但此示例實現Runnable

附錄:您可以查看project這個MVC體系結構的示例。它還展示瞭如何在不使用JAR Bundler的情況下構建Mac OS應用程序包。有關MVC的更多信息,請參閱here

另外,這個例子展示了一種自動滾動JTable的方法。點擊大拇指暫停滾動;釋放以恢復。

附錄:您的應用程序在啓動時滯後10秒。由於這是Controller睡眠的確切時間,所以肯定會睡在EDT上。 sscce將是dispositive。相反,在另一個線程上工作並更新EDT上的模型。 SwingWorker有自動執行的process()方法,或者您可以使用invokeLater(),如下所示。在您的應用程序正確同步之前,Apple的事件無法運行。

附錄:您可以調用isDispatchThread()中的Controller進行檢查。引用的項目包括一個帶有Mac應用程序的.dmg和一個ant文件,該文件通過目標dist2在原地構建了包

附錄:另請參閱here所示的替代方法。

enter image description here

import java.awt.BorderLayout; 
import java.awt.Dimension; 
import java.awt.EventQueue; 
import java.awt.Rectangle; 
import java.awt.event.ActionEvent; 
import java.awt.event.AdjustmentEvent; 
import java.awt.event.AdjustmentListener; 
import javax.swing.AbstractAction; 
import javax.swing.JButton; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.JProgressBar; 
import javax.swing.JScrollBar; 
import javax.swing.JScrollPane; 
import javax.swing.JTable; 
import javax.swing.table.DefaultTableModel; 

/** @seehttps://stackoverflow.com/questions/7519244 */ 
public class TableAddTest extends JPanel implements Runnable { 

    private static final int N_ROWS = 8; 
    private static String[] header = {"ID", "String", "Number", "Boolean"}; 
    private DefaultTableModel dtm = new DefaultTableModel(null, header) { 

     @Override 
     public Class<?> getColumnClass(int col) { 
      return getValueAt(0, col).getClass(); 
     } 
    }; 
    private JTable table = new JTable(dtm); 
    private JScrollPane scrollPane = new JScrollPane(table); 
    private JScrollBar vScroll = scrollPane.getVerticalScrollBar(); 
    private JProgressBar jpb = new JProgressBar(); 
    private int row; 
    private boolean isAutoScroll; 

    public TableAddTest() { 
     this.setLayout(new BorderLayout()); 
     jpb.setIndeterminate(true); 
     this.add(jpb, BorderLayout.NORTH); 
     Dimension d = new Dimension(320, N_ROWS * table.getRowHeight()); 
     table.setPreferredScrollableViewportSize(d); 
     for (int i = 0; i < N_ROWS; i++) { 
      addRow(); 
     } 
     scrollPane.setVerticalScrollBarPolicy(
      JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); 
     vScroll.addAdjustmentListener(new AdjustmentListener() { 

      @Override 
      public void adjustmentValueChanged(AdjustmentEvent e) { 
       isAutoScroll = !e.getValueIsAdjusting(); 
      } 
     }); 
     this.add(scrollPane, BorderLayout.CENTER); 
     JPanel panel = new JPanel(); 
     panel.add(new JButton(new AbstractAction("Add Row") { 

      @Override 
      public void actionPerformed(ActionEvent e) { 
       addRow(); 
      } 
     })); 
     this.add(panel, BorderLayout.SOUTH); 
    } 

    private void addRow() { 
     char c = (char) ('A' + row++ % 26); 
     dtm.addRow(new Object[]{ 
       Character.valueOf(c), 
       String.valueOf(c) + String.valueOf(row), 
       Integer.valueOf(row), 
       Boolean.valueOf(row % 2 == 0) 
      }); 
    } 

    private void scrollToLast() { 
     if (isAutoScroll) { 
      int last = table.getModel().getRowCount() - 1; 
      Rectangle r = table.getCellRect(last, 0, true); 
      table.scrollRectToVisible(r); 
     } 
    } 

    @Override 
    public void run() { 
     while (true) { 
      EventQueue.invokeLater(new Runnable() { 

       @Override 
       public void run() { 
        addRow(); 
       } 
      }); 
      EventQueue.invokeLater(new Runnable() { 

       @Override 
       public void run() { 
        scrollToLast(); 
       } 
      }); 
      try { 
       Thread.sleep(1000); // simulate latency 
      } catch (InterruptedException ex) { 
       System.err.println(ex); 
      } 
     } 
    } 

    public static void main(String[] args) { 
     EventQueue.invokeLater(new Runnable() { 

      @Override 
      public void run() { 
       JFrame f = new JFrame(); 
       f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
       TableAddTest nlt = new TableAddTest(); 
       f.add(nlt); 
       f.pack(); 
       f.setLocationRelativeTo(null); 
       f.setVisible(true); 
       new Thread(nlt).start(); 
      } 
     }); 
    } 
} 
+0

我懷疑捆綁只是暴露了潛在的同步問題。 – trashgod

+0

如果'addRow()'沒有從GUI類中調用,你能解釋一下如何做到這一點嗎? – kba

+0

外觀極好模擬+1 – mKorbel

2

這裏的@克列奧帕特拉的example在連續運行Controller接受doWork()新條目的變化,而SwingWorker在其後臺線程中異步處理pending條目。 ArrayBlockingQueue處理同步。

import java.awt.EventQueue; 
import java.util.List; 
import java.util.Random; 
import java.util.concurrent.ArrayBlockingQueue; 
import java.util.concurrent.BlockingQueue; 
import javax.swing.JFrame; 
import javax.swing.JScrollPane; 
import javax.swing.JTable; 
import javax.swing.SwingWorker; 
import javax.swing.table.DefaultTableModel; 

public class GUI { 

    private static final Random rnd = new Random(); 
    private JFrame frame = new JFrame(); 
    private DefaultTableModel model = new DefaultTableModel(); 
    private JTable table = new JTable(model); 
    private JScrollPane pane = new JScrollPane(table); 

    public GUI() { 
     model.addColumn("Name"); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     frame.add(pane); 
     frame.pack(); 
     frame.setVisible(true); 
    } 

    public void addRow(String name) { 
     model.addRow(new Object[]{name}); 
    } 

    /** 
    * Controller is a SwingWorker. 
    */ 
    private static class Controller extends SwingWorker<Void, String> { 

     private static final int MAX = 5; 
     private GUI gui; 
     private BlockingQueue<String> pending = 
      new ArrayBlockingQueue<String>(MAX); 

     public Controller() { 
      EventQueue.invokeLater(new Runnable() { 

       @Override 
       public void run() { 
        gui = new GUI(); 
       } 
      }); 
     } 

     private void doWork(String newLine) { 
      try { 
       pending.put(newLine); 
      } catch (InterruptedException e) { 
       e.printStackTrace(System.err); 
      } 
     } 

     @Override 
     protected Void doInBackground() throws Exception { 
      while (true) { 
       // may block if nothing pending 
       publish(pending.take()); 
       try { 
        Thread.sleep(rnd.nextInt(500)); // simulate latency 
       } catch (InterruptedException e) { 
        e.printStackTrace(System.err); 
       } 
      } 
     } 

     @Override 
     protected void process(List<String> chunks) { 
      for (String object : chunks) { 
       gui.addRow(object); 
      } 
     } 
    } 

    /** 
    * Exercise the Controller. 
    */ 
    private static class Adapter implements Runnable { 

     private Controller controller; 

     private Adapter(Controller controller) { 
      this.controller = controller; 
     } 

     @Override 
     public void run() { 
      controller.execute(); 
      int i = 0; 
      while (true) { 
       // may block if Controller busy 
       controller.doWork("Line " + (++i)); 
       try { 
        Thread.sleep(rnd.nextInt(500)); // simulate latency 
       } catch (InterruptedException e) { 
        e.printStackTrace(System.err); 
       } 
      } 
     } 
    } 

    public static void main(String[] args) { 
     System.out.println("Initializing controller"); 
     // Could run on inital thread via 
     // new Adapter(new Controller()).run(); 
     // but we'll start a new one 
     new Thread(new Adapter(new Controller())).start(); 
    } 
} 
+0

感謝您的回答,但我完全從我的實際項目中刪除了「pending」。 :-) – kba

+0

謝謝 - ArrayBlockingQueue是我今天學習的東西:-)和+1在美國東部時間創建用戶界面,我的健忘是傳奇... – kleopatra

+0

@kleopatra:我總是歡迎你的見解。我很高興你發現'ArrayBlockingQueue'有趣。我更新了代碼以使用接口['BlockingQueue'](http://download.oracle.com/javase/7/docs/api/java/util/concurrent/BlockingQueue.html),它的實現各種用例。 – trashgod

相關問題