我正在JavaFX中编写一个应用程序,需要偶尔加载大约1,000,000行(或更多)的大型CSV文件。
当用户单击按钮开始加载文件时,将启动服务以加载内容,同时显示进度/取消对话框。服务中的call()
方法基本上是一个while循环,在每次迭代时从CSV文件加载另一行。
问题在于,当我启动服务时,进度条(不确定样式)变得不稳定。拖动对话框也是生涩和滞后的。
我在网上寻找解决方案并没有好运,但我发现最接近的是将Thread.sleep()
置于循环中,让GC等其他东西有机会赶上来。
此解决方案似乎减少/消除了口吃,但它会增加大量时间来加载数据。我也猜测不同处理器之间的确切睡眠时间会有所不同。
有没有办法动态计算出多久/经常睡觉?或者调用一些方法,这些方法会阻塞足够长的时间来保持GUI响应?
我的服务代码:
public class CSVLoadingService extends Service<List<ObservableList<DoubleProperty>>> {
private ObjectProperty<File> srcFile = new SimpleObjectProperty<>();
private IntegerProperty startIndex = new SimpleIntegerProperty(0);
private ObjectProperty<Character> csvDelimeter = new SimpleObjectProperty(CSVParser.DEFAULT_SEPARATOR);
private DoubleProperty invalidCSVReplacement = new SimpleDoubleProperty(0);
private ObjectProperty<Dialog> dialog = new SimpleObjectProperty<>(null);
@Override
protected Task<List<ObservableList<DoubleProperty>>> createTask() {
return new Task<List<ObservableList<DoubleProperty>>>() {
final ObjectProperty<File> _srcFile = srcFile;
final IntegerProperty _startIndex = startIndex;
final ObjectProperty<Character> _csvDelimeter = csvDelimeter;
final DoubleProperty _invalidCSVReplacement = invalidCSVReplacement;
@Override
protected ObservableList<ObservableList<DoubleProperty>> call() throws Exception {
if (_startIndex.getValue() < 0)
throw new IllegalArgumentException("Start index can't be negative.");
if (_srcFile.getValue() == null)
throw new IllegalStateException("File can't be null.");
final ObservableList<ObservableList<DoubleProperty>> result = FXCollections.observableArrayList();
// Read the data from the CSV file.
try (final CSVReader reader = new CSVReader(new BufferedReader(new FileReader(_srcFile.getValue())),
_csvDelimeter.getValue(),
CSVParser.DEFAULT_QUOTE_CHARACTER,
_startIndex.getValue()))
{
// Read first line.
String[] csvLine = reader.readNext();
// If there is actually data, then read the rest of it.
if (csvLine == null || csvLine.length == 0) {
result.clear();
} else {
// Create columns.
for (String value : csvLine) {
result.add(FXCollections.observableArrayList());
}
// Parse the CSV reads and add them to the columns.
int iteration = 0;
do {
int i = 0;
for (String value : csvLine) {
// Convert the string to a number and add it to the column.
try {
result.get(i).add(new SimpleDoubleProperty(Double.parseDouble(value)));
} catch (NumberFormatException|NullPointerException e) {
result.get(i).add(_invalidCSVReplacement);
}
}
iteration++;
} while (!isCancelled() && null != (csvLine = reader.readNext()));
}
}
return result;
}
};
}
@Override
protected void succeeded() {
super.succeeded();
if (dialog.getValue() != null) {
dialog.getValue().close();
}
}
@Override
protected void failed() {
super.failed();
if (dialog.getValue() != null) {
dialog.getValue().close();
}
}