Увеличить прогресс ProgressBar в JavaFX с помощью нескольких потоков

#java #javafx #download #progress-bar

#java #javafx #Скачать #индикатор выполнения

Вопрос:

Я создаю приложение JavaFX, которое будет отправлять несколько файлов по сети, и я хочу иметь индикатор выполнения для количества отправленных байтов. У меня уже есть общий размер файлов, которые собираются быть отправлены, однако я не знаю, как увеличить индикатор выполнения.

Проблема в том, что я отправляю файлы одновременно, поэтому я не могу просто установить прогресс в своем классе загрузки, так как это будет перезаписано другой угрозой.

Вот как выглядит мой класс загрузки:

         iStream = socket.getInputStream();
        bufferSize = socket.getReceiveBufferSize();
        fos = new FileOutputStream(file);
        bos = new BufferedOutputStream(fos);
        byte[] bytes = new byte[bufferSize];
        int count;
        while ((count = is.read(bytes)) >= 0) {
            bos.write(bytes, 0, count);
        }
        bos.close();
        is.close();
  

И я выполняю класс следующим образом:

 List<Callable<String>> downloadTasks = new ArrayList<>();
Callable<String> download = new Download(new File(path), portnumber);
downloadTasks.add(download);
es.invokeAll(downloadTasks);
  

У меня есть общее количество байтов в переменной long totalSize в обоих классах.

Можете ли вы, пожалуйста, помочь мне в том, как я мог бы достичь индикатора выполнения байтов? Спасибо

Ответ №1:

Вы можете обновлять индикатор выполнения только из одного потока: потока приложения FX. Таким образом, проблема с потоками является спорной; вы можете просто увеличить свойство, представляющее общее количество загруженных байтов, и организовать его обновление только из этого потока.

Итак, что-то вроде:

 public class UploadProgressMonitor {

    private final long totalBytes ;
    private long downloadedBytes ;
    private final ReadOnlyDoubleWrapper progress = new ReadOnlyDoubleWrapper() ;

    public UploadProgressMonitor(long totalBytes) {
         this.totalBytes = totalBytes ;
    }

    private void doIncrement(long bytes) {
        downloadedBytes  = bytes ;
        progress.set(1.0 * downloadedBytes / totalBytes);
    }

    public void increment(long bytes) {
        if (Platform.isFxApplicationThread()) {
            doIncrement(bytes);
        } else {
            Platform.runLater(() -> doIncrement(bytes));
        }
    }

    public ReadOnlyDoubleProperty progressProperty() {
        return progress.getReadOnlyProperty() ;
    }

    public final double getProgress() {
        return progressProperty().get();
    }
}
  

Теперь вы можете делать такие вещи, как:

 long totalBytes = ... ;
UploadProgressMonitor monitor = new UploadProgressMonitor(totalBytes);
progressBar.progressProperty().bind(monitor.progressProperty());
  

и если вы дадите каждой задаче ссылку на UploadProgressMonitor то, что ей просто нужно сделать:

     int count;
    while ((count = is.read(bytes)) >= 0) {
        bos.write(bytes, 0, count);
        monitor.increment(count);
    }
  

Комментарии:

1. Спасибо, это работает, но проблема в том, что индикатор выполнения останавливается примерно на 90%. Если я вычислю размер int count после загрузки, это составит около 90% от общего размера в байтах, сообщаемого OS / Files.size(). Есть ли у вас какие-либо советы о том, как это исправить?

2. Я думаю, что это как-то связано с потоками. Если я отправлю только 1 файл, он будет равен 100%. Если я отправлю два одинаковых файла, это составит около 50%.

3. @Peter Нет … для отладки вы можете проверить count , что для каждой загрузки равен ожидаемому общему размеру для отдельного файла. Убедитесь, что вы правильно вычисляете итоговые значения, и убедитесь, что каждый раз, когда вы отправляете буфер, вы увеличиваете монитор (например, убедитесь, что вы не пропустили последнюю или какую-либо подобную ошибку). Это просто звучит так, как будто в вашем коде есть ошибка. Опять же, опубликованный мной код в основном однопоточный.

4. Извините, я это исправил. Это работает просто отлично. Я допустил довольно серьезную ошибку и создал новый монитор прогресса загрузки для каждого потока….