2014-05-21 21 views
2

在我的應用程序中,我生成了一個報告。手術時間從幾秒到幾個小時。要通知用戶我使用ProgressMonitorDialog。 總是在大約70分鐘後拋出InvocationTargetException。我不知道爲什麼會發生這種情況。ProgressMonitorDialog中的Cancel按鈕和InvocationTargetException

try { 
    new ProgressMonitorDialog(shell).run(true, true, new IRunnableWithProgress() { 
     @Override 
     public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { 
      monitor.beginTask("Something...", IProgressMonitor.UNKNOWN); 
      controller.generate(model); 
      monitor.done(); 
     } 
    }); 
} catch (InvocationTargetException | InterruptedException e) { 
    logger.error(e); 
} 

堆棧跟蹤:

java.lang.reflect.InvocationTargetException 
    at org.eclipse.jface.operation.ModalContext.run(ModalContext.java:421) 
    at org.eclipse.jface.dialogs.ProgressMonitorDialog.run(ProgressMonitorDialog.java:507) 
    at pl.edu.prz.allegroapi.gui.report.ProductReport$4.widgetSelected(ProductReport.java:323) 
    at org.eclipse.swt.widgets.TypedListener.handleEvent(Unknown Source) 
    at org.eclipse.swt.widgets.EventTable.sendEvent(Unknown Source) 
    at org.eclipse.swt.widgets.Widget.sendEvent(Unknown Source) 
    at org.eclipse.swt.widgets.Display.runDeferredEvents(Unknown Source) 
    at org.eclipse.swt.widgets.Display.readAndDispatch(Unknown Source) 
    at pl.edu.prz.allegroapi.gui.report.ProductReport.open(ProductReport.java:91) 
    at pl.edu.prz.allegroapi.gui.MainWindow$2.widgetSelected(MainWindow.java:126) 
    at org.eclipse.swt.widgets.TypedListener.handleEvent(Unknown Source) 
    at org.eclipse.swt.widgets.EventTable.sendEvent(Unknown Source) 
    at org.eclipse.swt.widgets.Widget.sendEvent(Unknown Source) 
    at org.eclipse.swt.widgets.Display.runDeferredEvents(Unknown Source) 
    at org.eclipse.swt.widgets.Display.readAndDispatch(Unknown Source) 
    at pl.edu.prz.allegroapi.gui.MainWindow.open(MainWindow.java:75) 
    at pl.edu.prz.allegroapi.tasks.CreateGuiTask.doStartup(CreateGuiTask.java:46) 
    at pl.edu.prz.allegroapi.tasks.CreateGuiTask.access$0(CreateGuiTask.java:42) 
    at pl.edu.prz.allegroapi.tasks.CreateGuiTask$1.run(CreateGuiTask.java:31) 
    at java.lang.Thread.run(Unknown Source) 
Caused by: org.eclipse.swt.SWTException: Invalid thread access 
    at org.eclipse.swt.SWT.error(Unknown Source) 
    at org.eclipse.swt.SWT.error(Unknown Source) 
    at org.eclipse.swt.SWT.error(Unknown Source) 
    at org.eclipse.swt.widgets.Widget.error(Unknown Source) 
    at org.eclipse.swt.widgets.Widget.checkWidget(Unknown Source) 
    at org.eclipse.swt.widgets.Dialog.checkParent(Unknown Source) 
    at org.eclipse.swt.widgets.Dialog.<init>(Unknown Source) 
    at org.eclipse.swt.widgets.MessageBox.<init>(Unknown Source) 
    at pl.edu.prz.allegroapi.gui.report.ProductReportController.generate(ProductReportController.java:59) 
    at pl.edu.prz.allegroapi.gui.report.ProductReport$4$1.run(ProductReport.java:335) 
    at org.eclipse.jface.operation.ModalContext$ModalContextThread.run(ModalContext.java:121) 

第二個問題是與工作進度的窗口取消。我知道按下「取消」按鈕,方法isCanceled()返回true。我嘗試了以下解決方案,但它無法工作,因爲變量退出是最終的。

Boolean exit = false; 
display.asyncExec(new Runnable() { 
    public void run() { 
     controller.generate(model); 
     exit=true; 
    } 
}); 
try { 
    new ProgressMonitorDialog(shell).run(true, true, new IRunnableWithProgress() { 
     @Override 
     public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { 
      try { 
       monitor.beginTask("Something...", IProgressMonitor.UNKNOWN); 
       while(!monitor.isCanceled() && !exit) { 
        Thread.sleep(1000); 
       } 
      } finally { 
       monitor.done(); 
      } 
     } 
    }); 
} catch (InvocationTargetException | InterruptedException e) { 
    e.printStackTrace(); 
} 
+0

對我的答案有何反饋? – Baz

+0

無效訪問問題已得到解決。消息框顯示異常情況下的generate()方法。不幸的是,我們仍然有可能取消長時間操作的問題(問題第二部分描述的問題)。 – Giver

+0

更新了我的答案。 – Baz

回答

2

您遇到了最常見的SWT異常,即「無效的線程訪問」。

它基本上意味着您正在嘗試從不是UI線程的線程更新UI。這在SWT中是不允許的,除非你使用特定的技術來做到這一點。

您可以閱讀更多關於它in the official wiki


您發佈的代碼似乎是確定,你的問題可能是在controller.generate(model);別處。沒有看到相關的代碼,我無法幫助你。

在此期間,這是你應該如何與UI從非UI線程交互:

Display.getDefault().asyncExec(new Runnable() { 
    public void run() { 
     ... do any work that updates the screen ... 
    } 
}); 

UPDATE

好吧,這裏是一個更新應告訴你ProgressMonitorDialog應該如何與Display#asyncExec()結合使用:

public static void main(String[] args) 
{ 
    final Display display = new Display(); 
    final Shell shell = new Shell(display); 
    shell.setText("StackOverflow"); 
    shell.setLayout(new GridLayout(1, false)); 

    final Label label = new Label(shell, SWT.NONE); 

    Button button = new Button(shell, SWT.PUSH); 
    button.setText("Start"); 
    button.addListener(SWT.Selection, new Listener() 
    { 
     @Override 
     public void handleEvent(Event arg0) 
     { 
      try 
      { 
       new ProgressMonitorDialog(shell).run(true, true, new IRunnableWithProgress() 
       { 
        @Override 
        public void run(final IProgressMonitor monitor) throws InvocationTargetException, InterruptedException 
        { 
         try 
         { 
          monitor.beginTask("Something...", IProgressMonitor.UNKNOWN); 

          for (int i = 0; i < 100; i++) 
          { 
           /* Check if the monitor has been canceled */ 
           if (monitor.isCanceled()) 
            return; 

           try 
           {/* Only wrap the UI interaction in the asyncExec */ 
            doFancyUIStuff(label, i); 
            Thread.sleep(100); 
           } 
           catch (InterruptedException e) 
           { 
            e.printStackTrace(); 
           } 
          } 
         } 
         finally 
         { 
          monitor.done(); 
         } 
        } 
       }); 
      } 
      catch (InvocationTargetException e) 
      { 
       e.printStackTrace(); 
      } 
      catch (InterruptedException e) 
      { 
       e.printStackTrace(); 
      } 
     } 
    }); 

    shell.pack(); 
    shell.open(); 

    while (!shell.isDisposed()) 
    { 
     while (!display.readAndDispatch()) 
     { 
      display.sleep(); 
     } 
    } 
} 

private static void doFancyUIStuff(final Label label, final int index) 
{ 
    Display.getDefault().asyncExec(new Runnable() 
    { 
     @Override 
     public void run() 
     { 
      label.setText(index + ""); 
      label.getParent().layout(); 
     } 
    }); 
} 

請注意,只有實際的UI交互被包裝在Display.asyncExec()中。這樣,您仍然可以在每次迭代中檢查monitor.isCanceled()

+0

我試過你的答案。我不得不把假設置在fork中,否則我得到SWT線程錯誤。 asyncExec由於某種原因未被激活,並且無法按下取消按鈕。可能所有問題的根源在於分叉,但無法找到答案。 [鏈接](http://stackoverflow.com/questions/26756068/eclipse-rcp-stopping-a-thread-job-that-freezes) – 2c00L

相關問題