我正在使用依賴於嵌入式H2數據庫的swing應用程序。因爲我不想將數據庫與應用程序捆綁在一起(數據庫經常更新,我希望應用程序的新用戶以最近的副本開始),所以我已經實現了一個解決方案,它將數據庫的壓縮副本第一次啓動應用程序並提取它。由於提取過程可能很慢,因此我添加了一個ProgressMonitorInputStream以顯示提取過程的進度 - 不幸的是,當提取開始時,進度對話框顯示,但根本沒有更新。似乎事件正在進入事件調度線程。下面是方法:在Swing中使用ProgressMonitorInputStream監視壓縮文件解壓縮時未更新UI
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;
}
這種方法獲取事件分派線程(SwingUtilities.isEventDispatchThread()返回true),使UI組件應更新上調用。我還沒有將它作爲SwingWorker實現,因爲在我可以繼續進行程序的初始化之前,我需要等待提取。在應用程序的主JFrame可見之前調用此方法。我不會不會基於SwingWorker +屬性的解決方案更改偵聽器 - 我認爲ProgressMonitorInputStream正是我需要的,但我想我沒有做正確的事情。我正在使用Sun JDK 1.6.18。任何幫助將不勝感激。
我認爲你在這裏說的是什麼,但我認爲Swing工程師可能記住輸入流通常是循環讀取的。如果循環完全佔用了EDT,那意味着這個ProgressMonitorInputStream總是沒用...... – 2010-05-12 09:45:11
我明白你在說什麼。我也無法真正看到用例,因爲如果你在EDT以外的線程上使用它,它會在底層的JProgressBar上調用'setValue'(除此之外),這本身就是打破了Swing的規則。但是它並沒有改變現實,即如果你阻止了EDT,你的GUI不會更新。你不應該在美國東部時間做I/O。另外,我在PMIS上搜索的每個示例都使用主線程或者swing工作人員從流中進行實際讀取。 – Ash 2010-05-12 10:38:31
我的問題是,我必須等待SwingWorker線程完成,然後才能使用init序列繼續。如果我在美國東部時間召集加入,情況將會和現在一樣糟糕。大多數情況下,當我使用監視器時,我有一些用戶按下的按鈕 - 這會啓動一個工作者線程,禁用一些UI元素,以便用戶強制等待線程完成。但是由於這種提取發生在沒有UI元素可見的地方,我不能這樣玩,我也不會引入不必要的對話來緩解這種情況。 – 2010-05-12 13:13:11