/* * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */package javax.swing; import java.lang.ref.WeakReference;import java.security.AccessController;import java.security.PrivilegedAction;import java.beans.PropertyChangeListener;import java.beans.PropertyChangeSupport;import java.beans.PropertyChangeEvent;import java.util.List;import java.util.concurrent.*;import java.util.concurrent.locks.*; import java.awt.event.*; import javax.swing.SwingUtilities; import sun.awt.AppContext;import sun.swing.AccumulativeRunnable; /** * An abstract class to perform lengthy GUI-interaction tasks in a * background thread. Several background threads can be used to execute such * tasks. However, the exact strategy of choosing a thread for any particular * {@code SwingWorker} is unspecified and should not be relied on. * <p> * When writing a multi-threaded application using Swing, there are * two constraints to keep in mind: * (refer to * <a href="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html"> * How to Use Threads * </a> for more details): * <ul> * <li> Time-consuming tasks should not be run on the <i>Event * Dispatch Thread</i>. Otherwise the application becomes unresponsive. * </li> * <li> Swing components should be accessed on the <i>Event * Dispatch Thread</i> only. * </li> * </ul> * * <p> * * <p> * These constraints mean that a GUI application with time intensive * computing needs at least two threads: 1) a thread to perform the lengthy * task and 2) the <i>Event Dispatch Thread</i> (EDT) for all GUI-related * activities. This involves inter-thread communication which can be * tricky to implement. * * <p> * {@code SwingWorker} is designed for situations where you need to have a long * running task run in a background thread and provide updates to the UI * either when done, or while processing. * Subclasses of {@code SwingWorker} must implement * the {@link #doInBackground} method to perform the background computation. * * * <p> * <b>Workflow</b> * <p> * There are three threads involved in the life cycle of a * {@code SwingWorker} : * <ul> * <li> * <p> * <i>Current</i> thread: The {@link #execute} method is * called on this thread. It schedules {@code SwingWorker} for the execution on a * <i>worker</i> * thread and returns immediately. One can wait for the {@code SwingWorker} to * complete using the {@link #get get} methods. * <li> * <p> * <i>Worker</i> thread: The {@link #doInBackground} * method is called on this thread. * This is where all background activities should happen. To notify * {@code PropertyChangeListeners} about bound properties changes use the * {@link #firePropertyChange firePropertyChange} and * {@link #getPropertyChangeSupport} methods. By default there are two bound * properties available: {@code state} and {@code progress}. * <li> * <p> * <i>Event Dispatch Thread</i>: All Swing related activities occur * on this thread. {@code SwingWorker} invokes the * {@link #process process} and {@link #done} methods and notifies * any {@code PropertyChangeListeners} on this thread. * </ul> * * <p> * Often, the <i>Current</i> thread is the <i>Event Dispatch * Thread</i>. * * * <p> * Before the {@code doInBackground} method is invoked on a <i>worker</i> thread, * {@code SwingWorker} notifies any {@code PropertyChangeListeners} about the * {@code state} property change to {@code StateValue.STARTED}. After the * {@code doInBackground} method is finished the {@code done} method is * executed. Then {@code SwingWorker} notifies any {@code PropertyChangeListeners} * about the {@code state} property change to {@code StateValue.DONE}. * * <p> * {@code SwingWorker} is only designed to be executed once. Executing a * {@code SwingWorker} more than once will not result in invoking the * {@code doInBackground} method twice. * * <p> * <b>Sample Usage</b> * <p> * The following example illustrates the simplest use case. Some * processing is done in the background and when done you update a Swing * component. * * <p> * Say we want to find the "Meaning of Life" and display the result in * a {@code JLabel}. * * <pre> * final JLabel label; * class MeaningOfLifeFinder extends SwingWorker<String, Object> { * {@code @Override} * public String doInBackground() { * return findTheMeaningOfLife(); * } * * {@code @Override} * protected void done() { * try { * label.setText(get()); * } catch (Exception ignore) { * } * } * } * * (new MeaningOfLifeFinder()).execute(); * </pre> * * <p> * The next example is useful in situations where you wish to process data * as it is ready on the <i>Event Dispatch Thread</i>. * * <p> * Now we want to find the first N prime numbers and display the results in a * {@code JTextArea}. While this is computing, we want to update our * progress in a {@code JProgressBar}. Finally, we also want to print * the prime numbers to {@code System.out}. * <pre> * class PrimeNumbersTask extends * SwingWorker<List<Integer>, Integer> { * PrimeNumbersTask(JTextArea textArea, int numbersToFind) { * //initialize * } * * {@code @Override} * public List<Integer> doInBackground() { * while (! enough && ! isCancelled()) { * number = nextPrimeNumber(); * publish(number); * setProgress(100 * numbers.size() / numbersToFind); * } * } * return numbers; * } * * {@code @Override} * protected void process(List<Integer> chunks) { * for (int number : chunks) { * textArea.append(number + "\n"); * } * } * } * * JTextArea textArea = new JTextArea(); * final JProgressBar progressBar = new JProgressBar(0, 100); * PrimeNumbersTask task = new PrimeNumbersTask(textArea, N); * task.addPropertyChangeListener( * new PropertyChangeListener() { * public void propertyChange(PropertyChangeEvent evt) { * if ("progress".equals(evt.getPropertyName())) { * progressBar.setValue((Integer)evt.getNewValue()); * } * } * }); * * task.execute(); * System.out.println(task.get()); //prints all prime numbers we have got * </pre> * * <p> * Because {@code SwingWorker} implements {@code Runnable}, a * {@code SwingWorker} can be submitted to an * {@link java.util.concurrent.Executor} for execution. * * @author Igor Kushnirskiy * * @param <T> the result type returned by this {@code SwingWorker's} * {@code doInBackground} and {@code get} methods * @param <V> the type used for carrying out intermediate results by this * {@code SwingWorker's} {@code publish} and {@code process} methods * * @since 1.6 */public abstract class SwingWorker<T, V> implements RunnableFuture<T> { /** * number of worker threads. */ private static final int MAX_WORKER_THREADS = 10; /** * current progress. */ private volatile int progress; /** * current state. */ private volatile StateValue state; /** * everything is run inside this FutureTask. Also it is used as * a delegatee for the Future API. */ private final FutureTask<T> future; /** * all propertyChangeSupport goes through this. */ private final PropertyChangeSupport propertyChangeSupport; /** * handler for {@code process} mehtod. */ private AccumulativeRunnable<V> doProcess; /** * handler for progress property change notifications. */ private AccumulativeRunnable<Integer> doNotifyProgressChange; private final AccumulativeRunnable<Runnable> doSubmit = getDoSubmit(); /** * Values for the {@code state} bound property. * @since 1.6 */ public enum StateValue { /** * Initial {@code SwingWorker} state. */ PENDING, /** * {@code SwingWorker} is {@code STARTED} * before invoking {@code doInBackground}. */ STARTED, /** * {@code SwingWorker} is {@code DONE} * after {@code doInBackground} method * is finished. */ DONE } /** * Constructs this {@code SwingWorker}. */ public SwingWorker() { Callable<T> callable = new Callable<T>() { public T call() throws Exception { setState(StateValue.STARTED); return doInBackground(); } }; future = new FutureTask<T>(callable) { @Override protected void done() { doneEDT(); setState(StateValue.DONE); } }; state = StateValue.PENDING; propertyChangeSupport = new SwingWorkerPropertyChangeSupport(this); doProcess = null; doNotifyProgressChange = null; } /** * Computes a result, or throws an exception if unable to do so. * * <p> * Note that this method is executed only once. * * <p> * Note: this method is executed in a background thread. * * * @return the computed result * @throws Exception if unable to compute a result * */ protected abstract T doInBackground() throws Exception ; /** * Sets this {@code Future} to the result of computation unless * it has been cancelled. */ public final void run() { future.run(); } /** * Sends data chunks to the {@link #process} method. This method is to be * used from inside the {@code doInBackground} method to deliver * intermediate results * for processing on the <i>Event Dispatch Thread</i> inside the * {@code process} method. * * <p> * Because the {@code process} method is invoked asynchronously on * the <i>Event Dispatch Thread</i> * multiple invocations to the {@code publish} method * might occur before the {@code process} method is executed. For * performance purposes all these invocations are coalesced into one * invocation with concatenated arguments. * * <p> * For example: * * <pre> * publish("1"); * publish("2", "3"); * publish("4", "5", "6"); * </pre> * * might result in: * * <pre> * process("1", "2", "3", "4", "5", "6") * </pre> * * <p> * <b>Sample Usage</b>. This code snippet loads some tabular data and * updates {@code DefaultTableModel} with it. Note that it safe to mutate * the tableModel from inside the {@code process} method because it is * invoked on the <i>Event Dispatch Thread</i>. * * <pre> * class TableSwingWorker extends * SwingWorker<DefaultTableModel, Object[]> { * private final DefaultTableModel tableModel; * * public TableSwingWorker(DefaultTableModel tableModel) { * this.tableModel = tableModel; * } * * {@code @Override} * protected DefaultTableModel doInBackground() throws Exception { * for (Object[] row = loadData(); * ! isCancelled() && row != null; * row = loadData()) { * publish((Object[]) row); * } * return tableModel; * } * * {@code @Override} * protected void process(List<Object[]> chunks) { * for (Object[] row : chunks) { * tableModel.addRow(row); * } * } * } * </pre> * * @param chunks intermediate results to process * * @see #process * */ @SafeVarargs protected final void publish(V... chunks) { synchronized (this) { if (doProcess == null) { doProcess = new AccumulativeRunnable<V>() { @Override public void run(List<V> args) { process(args); } @Override protected void submit() { doSubmit.add(this); } }; } } doProcess.add(chunks); } /** * Receives data chunks from the {@code publish} method asynchronously on the * <i>Event Dispatch Thread</i>. * * <p> * Please refer to the {@link #publish} method for more details. * * @param chunks intermediate results to process * * @see #publish * */ protected void process(List<V> chunks) { } /** * Executed on the <i>Event Dispatch Thread</i> after the {@code doInBackground} * method is finished. The default * implementation does nothing. Subclasses may override this method to * perform completion actions on the <i>Event Dispatch Thread</i>. Note * that you can query status inside the implementation of this method to * determine the result of this task or whether this task has been cancelled. * * @see #doInBackground * @see #isCancelled() * @see #get */ protected void done() { } /** * Sets the {@code progress} bound property. * The value should be from 0 to 100. * * <p> * Because {@code PropertyChangeListener}s are notified asynchronously on * the <i>Event Dispatch Thread</i> multiple invocations to the * {@code setProgress} method might occur before any * {@code PropertyChangeListeners} are invoked. For performance purposes * all these invocations are coalesced into one invocation with the last * invocation argument only. * * <p> * For example, the following invokations: * * <pre> * setProgress(1); * setProgress(2); * setProgress(3); * </pre> * * might result in a single {@code PropertyChangeListener} notification with * the value {@code 3}. * * @param progress the progress value to set * @throws IllegalArgumentException is value not from 0 to 100 */ protected final void setProgress(int progress) { if (progress < 0 || progress > 100) { throw new IllegalArgumentException("the value should be from 0 to 100"); } if (this.progress == progress) { return; } int oldProgress = this.progress; this.progress = progress; if (! getPropertyChangeSupport().hasListeners("progress")) { return; } synchronized (this) { if (doNotifyProgressChange == null) { doNotifyProgressChange = new AccumulativeRunnable<Integer>() { @Override public void run(List<Integer> args) {Lines 1–500 of 866. Load more
/* * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package javax.swing; import java.lang.ref.WeakReference; import java.security.AccessController; import java.security.PrivilegedAction; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.beans.PropertyChangeEvent; import java.util.List; import java.util.concurrent.*; import java.util.concurrent.locks.*; import java.awt.event.*; import javax.swing.SwingUtilities; import sun.awt.AppContext; import sun.swing.AccumulativeRunnable; /** * An abstract class to perform lengthy GUI-interaction tasks in a * background thread. Several background threads can be used to execute such * tasks. However, the exact strategy of choosing a thread for any particular * {@code SwingWorker} is unspecified and should not be relied on. * <p> * When writing a multi-threaded application using Swing, there are * two constraints to keep in mind: * (refer to * <a href="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html"> * How to Use Threads * </a> for more details): * <ul> * <li> Time-consuming tasks should not be run on the <i>Event * Dispatch Thread</i>. Otherwise the application becomes unresponsive. * </li> * <li> Swing components should be accessed on the <i>Event * Dispatch Thread</i> only. * </li> * </ul> * * <p> * * <p> * These constraints mean that a GUI application with time intensive * computing needs at least two threads: 1) a thread to perform the lengthy * task and 2) the <i>Event Dispatch Thread</i> (EDT) for all GUI-related * activities. This involves inter-thread communication which can be * tricky to implement. * * <p> * {@code SwingWorker} is designed for situations where you need to have a long * running task run in a background thread and provide updates to the UI * either when done, or while processing. * Subclasses of {@code SwingWorker} must implement * the {@link #doInBackground} method to perform the background computation. * * * <p> * <b>Workflow</b> * <p> * There are three threads involved in the life cycle of a * {@code SwingWorker} : * <ul> * <li> * <p> * <i>Current</i> thread: The {@link #execute} method is * called on this thread. It schedules {@code SwingWorker} for the execution on a * <i>worker</i> * thread and returns immediately. One can wait for the {@code SwingWorker} to * complete using the {@link #get get} methods. * <li> * <p> * <i>Worker</i> thread: The {@link #doInBackground} * method is called on this thread. * This is where all background activities should happen. To notify * {@code PropertyChangeListeners} about bound properties changes use the * {@link #firePropertyChange firePropertyChange} and * {@link #getPropertyChangeSupport} methods. By default there are two bound * properties available: {@code state} and {@code progress}. * <li> * <p> * <i>Event Dispatch Thread</i>: All Swing related activities occur * on this thread. {@code SwingWorker} invokes the * {@link #process process} and {@link #done} methods and notifies * any {@code PropertyChangeListeners} on this thread. * </ul> * * <p> * Often, the <i>Current</i> thread is the <i>Event Dispatch * Thread</i>. * * * <p> * Before the {@code doInBackground} method is invoked on a <i>worker</i> thread, * {@code SwingWorker} notifies any {@code PropertyChangeListeners} about the * {@code state} property change to {@code StateValue.STARTED}. After the * {@code doInBackground} method is finished the {@code done} method is * executed. Then {@code SwingWorker} notifies any {@code PropertyChangeListeners} * about the {@code state} property change to {@code StateValue.DONE}. * * <p> * {@code SwingWorker} is only designed to be executed once. Executing a * {@code SwingWorker} more than once will not result in invoking the * {@code doInBackground} method twice. * * <p> * <b>Sample Usage</b> * <p> * The following example illustrates the simplest use case. Some * processing is done in the background and when done you update a Swing * component. * * <p> * Say we want to find the "Meaning of Life" and display the result in * a {@code JLabel}. * * <pre> * final JLabel label; * class MeaningOfLifeFinder extends SwingWorker<String, Object> { * {@code @Override} * public String doInBackground() { * return findTheMeaningOfLife(); * } * * {@code @Override} * protected void done() { * try { * label.setText(get()); * } catch (Exception ignore) { * } * } * } * * (new MeaningOfLifeFinder()).execute(); * </pre> * * <p> * The next example is useful in situations where you wish to process data * as it is ready on the <i>Event Dispatch Thread</i>. * * <p> * Now we want to find the first N prime numbers and display the results in a * {@code JTextArea}. While this is computing, we want to update our * progress in a {@code JProgressBar}. Finally, we also want to print * the prime numbers to {@code System.out}. * <pre> * class PrimeNumbersTask extends * SwingWorker<List<Integer>, Integer> { * PrimeNumbersTask(JTextArea textArea, int numbersToFind) { * //initialize * } * * {@code @Override} * public List<Integer> doInBackground() { * while (! enough && ! isCancelled()) { * number = nextPrimeNumber(); * publish(number); * setProgress(100 * numbers.size() / numbersToFind); * } * } * return numbers; * } * * {@code @Override} * protected void process(List<Integer> chunks) { * for (int number : chunks) { * textArea.append(number + "\n"); * } * } * } * * JTextArea textArea = new JTextArea(); * final JProgressBar progressBar = new JProgressBar(0, 100); * PrimeNumbersTask task = new PrimeNumbersTask(textArea, N); * task.addPropertyChangeListener( * new PropertyChangeListener() { * public void propertyChange(PropertyChangeEvent evt) { * if ("progress".equals(evt.getPropertyName())) { * progressBar.setValue((Integer)evt.getNewValue()); * } * } * }); * * task.execute(); * System.out.println(task.get()); //prints all prime numbers we have got * </pre> * * <p> * Because {@code SwingWorker} implements {@code Runnable}, a * {@code SwingWorker} can be submitted to an * {@link java.util.concurrent.Executor} for execution. * * @author Igor Kushnirskiy * * @param <T> the result type returned by this {@code SwingWorker's} * {@code doInBackground} and {@code get} methods * @param <V> the type used for carrying out intermediate results by this * {@code SwingWorker's} {@code publish} and {@code process} methods * * @since 1.6 */ public abstract class SwingWorker<T, V> implements RunnableFuture<T> { /** * number of worker threads. */ private static final int MAX_WORKER_THREADS = 10; /** * current progress. */ private volatile int progress; /** * current state. */ private volatile StateValue state; /** * everything is run inside this FutureTask. Also it is used as * a delegatee for the Future API. */ private final FutureTask<T> future; /** * all propertyChangeSupport goes through this. */ private final PropertyChangeSupport propertyChangeSupport; /** * handler for {@code process} mehtod. */ private AccumulativeRunnable<V> doProcess; /** * handler for progress property change notifications. */ private AccumulativeRunnable<Integer> doNotifyProgressChange; private final AccumulativeRunnable<Runnable> doSubmit = getDoSubmit(); /** * Values for the {@code state} bound property. * @since 1.6 */ public enum StateValue { /** * Initial {@code SwingWorker} state. */ PENDING, /** * {@code SwingWorker} is {@code STARTED} * before invoking {@code doInBackground}. */ STARTED, /** * {@code SwingWorker} is {@code DONE} * after {@code doInBackground} method * is finished. */ DONE } /** * Constructs this {@code SwingWorker}. */ public SwingWorker() { Callable<T> callable = new Callable<T>() { public T call() throws Exception { setState(StateValue.STARTED); return doInBackground(); } }; future = new FutureTask<T>(callable) { @Override protected void done() { doneEDT(); setState(StateValue.DONE); } }; state = StateValue.PENDING; propertyChangeSupport = new SwingWorkerPropertyChangeSupport(this); doProcess = null; doNotifyProgressChange = null; } /** * Computes a result, or throws an exception if unable to do so. * * <p> * Note that this method is executed only once. * * <p> * Note: this method is executed in a background thread. * * * @return the computed result * @throws Exception if unable to compute a result * */ protected abstract T doInBackground() throws Exception ; /** * Sets this {@code Future} to the result of computation unless * it has been cancelled. */ public final void run() { future.run(); } /** * Sends data chunks to the {@link #process} method. This method is to be * used from inside the {@code doInBackground} method to deliver * intermediate results * for processing on the <i>Event Dispatch Thread</i> inside the * {@code process} method. * * <p> * Because the {@code process} method is invoked asynchronously on * the <i>Event Dispatch Thread</i> * multiple invocations to the {@code publish} method * might occur before the {@code process} method is executed. For * performance purposes all these invocations are coalesced into one * invocation with concatenated arguments. * * <p> * For example: * * <pre> * publish("1"); * publish("2", "3"); * publish("4", "5", "6"); * </pre> * * might result in: * * <pre> * process("1", "2", "3", "4", "5", "6") * </pre> * * <p> * <b>Sample Usage</b>. This code snippet loads some tabular data and * updates {@code DefaultTableModel} with it. Note that it safe to mutate * the tableModel from inside the {@code process} method because it is * invoked on the <i>Event Dispatch Thread</i>. * * <pre> * class TableSwingWorker extends * SwingWorker<DefaultTableModel, Object[]> { * private final DefaultTableModel tableModel; * * public TableSwingWorker(DefaultTableModel tableModel) { * this.tableModel = tableModel; * } * * {@code @Override} * protected DefaultTableModel doInBackground() throws Exception { * for (Object[] row = loadData(); * ! isCancelled() && row != null; * row = loadData()) { * publish((Object[]) row); * } * return tableModel; * } * * {@code @Override} * protected void process(List<Object[]> chunks) { * for (Object[] row : chunks) { * tableModel.addRow(row); * } * } * } * </pre> * * @param chunks intermediate results to process * * @see #process * */ @SafeVarargs protected final void publish(V... chunks) { synchronized (this) { if (doProcess == null) { doProcess = new AccumulativeRunnable<V>() { @Override public void run(List<V> args) { process(args); } @Override protected void submit() { doSubmit.add(this); } }; } } doProcess.add(chunks); } /** * Receives data chunks from the {@code publish} method asynchronously on the * <i>Event Dispatch Thread</i>. * * <p> * Please refer to the {@link #publish} method for more details. * * @param chunks intermediate results to process * * @see #publish * */ protected void process(List<V> chunks) { } /** * Executed on the <i>Event Dispatch Thread</i> after the {@code doInBackground} * method is finished. The default * implementation does nothing. Subclasses may override this method to * perform completion actions on the <i>Event Dispatch Thread</i>. Note * that you can query status inside the implementation of this method to * determine the result of this task or whether this task has been cancelled. * * @see #doInBackground * @see #isCancelled() * @see #get */ protected void done() { } /** * Sets the {@code progress} bound property. * The value should be from 0 to 100. * * <p> * Because {@code PropertyChangeListener}s are notified asynchronously on * the <i>Event Dispatch Thread</i> multiple invocations to the * {@code setProgress} method might occur before any * {@code PropertyChangeListeners} are invoked. For performance purposes * all these invocations are coalesced into one invocation with the last * invocation argument only. * * <p> * For example, the following invokations: * * <pre> * setProgress(1); * setProgress(2); * setProgress(3); * </pre> * * might result in a single {@code PropertyChangeListener} notification with * the value {@code 3}. * * @param progress the progress value to set * @throws IllegalArgumentException is value not from 0 to 100 */ protected final void setProgress(int progress) { if (progress < 0 || progress > 100) { throw new IllegalArgumentException("the value should be from 0 to 100"); } if (this.progress == progress) { return; } int oldProgress = this.progress; this.progress = progress; if (! getPropertyChangeSupport().hasListeners("progress")) { return; } synchronized (this) { if (doNotifyProgressChange == null) { doNotifyProgressChange = new AccumulativeRunnable<Integer>() { @Override public void run(List<Integer> args) {