我正在开发依赖于嵌入式 H2 数据库的 swing 应用程序。因为我不想将数据库与应用程序捆绑在一起(数据库经常更新,并且我希望应用程序的新用户从最近的副本开始),所以我实现了一个解决方案,该解决方案下载数据库的压缩副本第一次启动应用程序并提取它。由于提取过程可能很慢,我添加了 ProgressMonitorInputStream显示提取过程的进度 - 不幸的是,当提取开始时,会显示进度对话框,但根本没有更新。事件似乎正在传递到事件调度线程。方法如下:
public static String extractDbFromArchive(String pathToArchive) {
if (SwingUtilities.isEventDispatchThread()) {
System.out.println("Invoking on event dispatch thread");
}
// Get the current path, where the database will be extracted
String currentPath = System.getProperty("user.home") + File.separator + ".spellbook" + File.separator;
LOGGER.info("Current path: " + currentPath);
try {
//Open the archive
FileInputStream archiveFileStream = new FileInputStream(pathToArchive);
// Read two bytes from the stream before it used by CBZip2InputStream
for (int i = 0; i < 2; i++) {
archiveFileStream.read();
}
// Open the gzip file and open the output file
CBZip2InputStream bz2 = new CBZip2InputStream(new ProgressMonitorInputStream(
null,
"Decompressing " + pathToArchive,
archiveFileStream));
FileOutputStream out = new FileOutputStream(ARCHIVED_DB_NAME);
LOGGER.info("Decompressing the tar file...");
// Transfer bytes from the compressed file to the output file
byte[] buffer = new byte[1024];
int len;
while ((len = bz2.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
// Close the file and stream
bz2.close();
out.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException ex) {
ex.printStackTrace();
}
try {
TarInputStream tarInputStream = null;
TarEntry tarEntry;
tarInputStream = new TarInputStream(new ProgressMonitorInputStream(
null,
"Extracting " + ARCHIVED_DB_NAME,
new FileInputStream(ARCHIVED_DB_NAME)));
tarEntry = tarInputStream.getNextEntry();
byte[] buf1 = new byte[1024];
LOGGER.info("Extracting tar file");
while (tarEntry != null) {
//For each entry to be extracted
String entryName = currentPath + tarEntry.getName();
entryName = entryName.replace('/', File.separatorChar);
entryName = entryName.replace('\\', File.separatorChar);
LOGGER.info("Extracting entry: " + entryName);
FileOutputStream fileOutputStream;
File newFile = new File(entryName);
if (tarEntry.isDirectory()) {
if (!newFile.mkdirs()) {
break;
}
tarEntry = tarInputStream.getNextEntry();
continue;
}
fileOutputStream = new FileOutputStream(entryName);
int n;
while ((n = tarInputStream.read(buf1, 0, 1024)) > -1) {
fileOutputStream.write(buf1, 0, n);
}
fileOutputStream.close();
tarEntry = tarInputStream.getNextEntry();
}
tarInputStream.close();
} catch (Exception e) {
}
currentPath += "db" + File.separator + DB_FILE_NAME;
if (!currentPath.isEmpty()) {
LOGGER.info("DB placed in : " + currentPath);
}
return currentPath;
}
此方法在事件分派(dispatch)线程上调用(SwingUtilities.isEventDispatchThread() 返回 true),因此应更新 UI 组件。我还没有将其实现为 SwingWorker,因为无论如何我都需要等待提取,然后才能继续程序的初始化。在应用程序的主 JFrame 可见之前调用此方法。我不会基于 SwingWorker + 属性更改监听器的解决方案 - 我认为 ProgressMonitorInputStream 正是我所需要的,但我想我没有做正确的事情。我使用的是 Sun JDK 1.6.18。任何帮助将不胜感激。
最佳答案
当您在 EDT 上运行提取过程时,它将阻止对 GUI 的所有更新,甚至是进度监视器。这正是 SwingWorker
将提供帮助的情况。
实际上,您通过占用 EDT 来进行数据库提取来阻止绘制。任何 GUI 更新请求(例如对 repaint()
的调用)都将排队,但实际上重新绘制这些更新永远不会运行,因为 EDT 已经很忙。
它可能起作用的唯一方法是将处理卸载到另一个线程。 SwingWorker
让这件事变得更容易。
关于java - 在 Swing 中使用 ProgressMonitorInputStream 监视压缩文件解压缩时 UI 未更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2817235/