VAADIN - 从繁重的计算逻辑发送响应

时间:2016-11-28 21:12:35

标签: java vaadin vaadin7 java-threads

我的VAADIN应用程序中有一个带有mutliSelect(true)(Checkboxes)的OptionGroup。当我单击复选框字段时,在valueChange监听器中执行一些繁重的计算逻辑。我希望在执行此逻辑时阻止用户交互。所以我想使用轮询方法显示一个ProgressBar,其中indeterminate标志设置为true。

用于valueChange侦听器的逻辑如下所示

@Override
public void valueChange(Property.ValueChangeEvent event) {
    Thread valueChangeProcessorThread = new Thread(new ValueChangeProcessor(myComponent));
    valueChangeProcessorThread.start();
}

如您所见,执行的线程定义如下

private class ValueChangeProcessor implements Runnable {
    private final MyComponent myComponent;

    public ValueChangeProcessor(MyComponent myComponent) {
        this.myComponent = myComponent;
    }

    public synchronized MyComponent getMyComponent() {
        return myComponent;
    }

    @Override
    public void run() {
        getMyComponent().getUI().access(new Runnable() {
            @Override
            public void run() {
                showWaitIndicator();
            }
        });

        // Some heavy computation logic

        hideWaitIndicator();
    }
}

这里我使用UI.access()来显示不确定的ProgessBar。在showWaitIndicator()方法中可以使用这样做的逻辑。计算完成后,我通过调用hideWaitIndicator()方法隐藏ProgressBar。

在这里,有时showWaitIndicator()方法只有在执行计算和hideWaitIndicator()方法后才会执行。如何确保所有内容按照正确的顺序执行?

1 个答案:

答案 0 :(得分:0)

我在周末与同样的问题进行了斗争。这是我目前的实施方式。

重点是我使用attach侦听器来启动后台线程。

另请注意,此方法需要启用vaadin push才能生效。

用法:

VModalTaskDialog.execute(dialog -> {
  dialog.updateProgressIndeterminate(false);
  dialog.updateCancelable(true);
  dialog.updateTitle("Processing...");

  // simulate work
  for(int i = 0; i < 10; i++)
  {
    if(dialog.isCanceled())
    {
      System.out.println("canceled");
      return;
    }

    Thread.sleep(1000);
    dialog.updateDetails(i + " Details Details Details Details Details Details Details Details Details Details Details Details.");
    dialog.updateProgress(0.1f + i * 0.1f);
  }

  UI.getCurrent().access(() -> {
    // update UI from background task
  });
});

代码:

public class VModalTaskDialog
{
  @SuppressWarnings("unused")
  private static final Logger LOGGER = Logger.getLogger(VModalTaskDialog.class.getName());

  public static interface IModalTask
  {
    public void execute(VModalTaskDialog dialog) throws Exception;
  }

  public static void execute(IModalTask modalTask)
  {
    execute(500, 300, modalTask);
  }

  /**
   * dimensions required for centering on screen
   */
  public static void execute(int width, int height, IModalTask modalTask /* , Executable callback */)
  {
    VModalTaskDialog modalTaskDialog = new VModalTaskDialog();

    modalTaskDialog.m_modalTask = modalTask;

    modalTaskDialog.initAndStart(width, height);
  }

  private IModalTask  m_modalTask       = null;
  private Thread      m_modalTaskThread = null;

  private Window      m_window          = null;
  private Label       m_statusLabel     = null;
  private ProgressBar m_progressBar     = null;
  private Label       m_detailsLabel    = null;

  private Button      m_cancelButton    = null;
  private boolean     m_cancelable      = false;
  private boolean     m_canceled        = false;

  public VModalTaskDialog()
  {
    // nothing
  }

  private void initAndStart(int width, int height)
  {
    m_window = new Window();

    m_window.setModal(true);

    m_window.addCloseListener(e -> tryCancel());

    m_window.setWidth(width + "px");
    m_window.setHeight(height + "px");

    VerticalLayout contentLayout = new VerticalLayout();
    contentLayout.setSizeFull();

    {
      // show scroll bars if status or details overflow the available space
      Panel panel = new Panel();
      panel.setSizeFull();
      panel.setStyleName("borderless");

      {
        VerticalLayout layoutInPanel1 = new VerticalLayout();
        layoutInPanel1.setWidth("100%");

        // for some reason, the first vertical layout child of a panel creates a narrow margin
        // the second nested layout creates normal width margin
        VerticalLayout layoutInPanel2 = new VerticalLayout();
        layoutInPanel2.setWidth("100%");
        layoutInPanel2.setSpacing(true);
        layoutInPanel2.setMargin(true);

        {
          m_statusLabel = new Label();
          m_statusLabel.setStyleName("h3 no-margin");
          m_statusLabel.setWidth("100%"); // get label to wrap text
          m_statusLabel.setContentMode(ContentMode.HTML);
          layoutInPanel2.addComponent(m_statusLabel);
        }
        {
          m_progressBar = new ProgressBar();
          m_progressBar.setIndeterminate(true);
          m_progressBar.setStyleName("vaadin-modal-task-progress");
          m_progressBar.setWidth("100%");
          layoutInPanel2.addComponent(m_progressBar);
        }
        {
          m_detailsLabel = new Label();
          m_detailsLabel.setStyleName("vaadin-modal-task-details");
          m_detailsLabel.setWidth("100%"); // get label to wrap text
          m_detailsLabel.setContentMode(ContentMode.HTML);
          layoutInPanel2.addComponent(m_detailsLabel);
        }

        layoutInPanel1.addComponent(layoutInPanel2);
        panel.setContent(layoutInPanel1);
      }

      contentLayout.addComponent(panel);
      contentLayout.setExpandRatio(panel, 1f);
    }

    {

      HorizontalLayout buttonRowLayout = new HorizontalLayout();
      buttonRowLayout.setMargin(new MarginInfo(false, false, true, false));

      {
        m_cancelButton = new Button("Cancel");
        m_cancelButton.setEnabled(false);
        m_cancelButton.addClickListener(e -> tryCancel());

        buttonRowLayout.addComponent(m_cancelButton);
      }

      contentLayout.addComponent(buttonRowLayout);
      contentLayout.setComponentAlignment(buttonRowLayout, Alignment.BOTTOM_CENTER);
    }

    m_window.setContent(contentLayout);

    m_window.center();

    m_window.addAttachListener(e -> eventWindowAttached());

    // show dialog
    UI.getCurrent().addWindow(m_window);
  }

  private void eventWindowAttached()
  {
    //    LOGGER.log(Level.INFO, "modal task window attached, starting background thread");

    // start modal task in background
    m_modalTaskThread = new Thread(new Runnable() {
      @Override
      public void run()
      {
        runInThread();
      }
    });
    m_modalTaskThread.setName("modal-task-" + new Random().nextInt(Integer.MAX_VALUE));
    m_modalTaskThread.start();

    // waiting until modalTaskAction finishes
  }

  private void runInThread()
  {
    //    LOGGER.log(Level.INFO, "running modal task in thread");

    Throwable throwableFromTask = null;

    try
    {
      m_modalTask.execute(VModalTaskDialog.this);
    }
    catch(InterruptedException | InterruptedIOException e)
    {
      if(m_canceled)
      {
        //        LOGGER.log(Level.INFO, "canceled: " + t);
        // expected
      }
      else
      {
        // interruption without cancellation is unexpected
        throwableFromTask = e;
      }
    }
    catch(Throwable t)
    {
      // task failed
      throwableFromTask = t;
    }

    // close dialog
    safeClose();

    // maybe show exception
    try
    {
      if(throwableFromTask != null)
      {
        final Throwable finalThrowableFromTask = throwableFromTask;
        UI.getCurrent().access(() -> Notification.show("" + finalThrowableFromTask, Type.ERROR_MESSAGE));
      }
    }
    catch(Throwable t)
    {
      LOGGER.log(Level.WARNING, "failed to show modal task exception: " + t);
    }
  }

  private void safeClose()
  {
    try
    {
      //      LOGGER.log(Level.INFO, "closing modal task dialog");
      UI.getCurrent().access(() -> m_window.close());
    }
    catch(Throwable t)
    {
      LOGGER.log(Level.WARNING, "failed to close modal task dialog: " + t);
    }
  }

  /** update method, to be called from task thread */
  public void updateThreadName(String threadName)
  {
    m_modalTaskThread.setName(String.valueOf(threadName));
  }

  /** update method, to be called from task thread */
  public void updateThreadPriority(int priority)
  {
    m_modalTaskThread.setPriority(priority);
  }

  /** update method, to be called from task thread */
  public void updateCancelable(boolean cancelable)
  {
    m_cancelable = cancelable;

    UI.getCurrent().access(() -> m_cancelButton.setEnabled(cancelable));
  }

  /** update method, to be called from task thread */
  public void updateTitle(String title)
  {
    UI.getCurrent().access(() -> m_statusLabel.setValue(title));
  }

  /** update method, to be called from task thread */
  public void updateDetails(String details)
  {
    UI.getCurrent().access(() -> m_detailsLabel.setValue(details));
  }

  /** update method, to be called from task thread */
  public void updateProgress(float progress)
  {
    UI.getCurrent().access(() -> m_progressBar.setValue(Float.valueOf(progress)));
  }

  /** update method, to be called from task thread */
  public void updateProgressIndeterminate(boolean indeterminate)
  {
    UI.getCurrent().access(() -> m_progressBar.setIndeterminate(indeterminate));
  }

  private void tryCancel()
  {
    if(!m_cancelable)
    {
      return;
    }

    m_canceled = true;

    //    LOGGER.log(Level.INFO, "cancel: interrupting modal task thread");
    m_modalTaskThread.interrupt();
  }

  public boolean isCanceled()
  {
    return m_canceled;
  }
}

它看起来像这样:

dialog