在ProgressMonitorDialog和InvocationTargetException中取消按钮

时间:2014-05-21 09:44:59

标签: java multithreading swt

在我的应用程序中,我生成一个报告。操作时间从几秒到几个小时。要通知用户我使用ProgressMonitorDialog。 总是在大约70分钟之后抛出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。我尝试了以下解决方案,但它无法工作,因为变量exit将是最终的。

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();
}

1 个答案:

答案 0 :(得分:3)

您遇到了最常见的SWT异常,即&#34;无效的线程访问&#34;。

它基本上意味着您尝试从不是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 ...
    }
});

<强>更新

好的,这是一个更新,应该向您展示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();
        }
    });
}

请注意,只有实际的用户界面互动才会包含在Display.asyncExec()中。这样,您仍然可以在每次迭代中检查monitor.isCanceled()