Пользовательский интерфейс не обновляется при использовании ProgressMonitorInputStream в Swing для мониторинга распаковки сжатых файлов - PullRequest
2 голосов
/ 12 мая 2010

Я работаю над свинг-приложением, которое опирается на встроенную базу данных H2. Поскольку я не хочу связывать базу данных с приложением (база данных часто обновляется, и я хочу, чтобы новые пользователи приложения начинали с последней копии), я реализовал решение, которое загружает сжатую копию базы данных. Первый раз приложение запускается и извлекает его. Поскольку процесс извлечения может быть медленным, я добавил 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;
}

Этот метод вызывается в потоке диспетчеризации событий (SwingUtilities.isEventDispatchThread () возвращает true), поэтому компоненты пользовательского интерфейса должны быть обновлены. Я не реализовал это как SwingWorker, так как мне все равно нужно ждать извлечения, прежде чем я смогу приступить к инициализации программы. Этот метод вызывается до того, как основной JFrame приложения станет видимым. Я не хочу, чтобы решение на основе свойства SwingWorker + изменило слушателей - я думаю, что ProgressMonitorInputStream - это именно то, что мне нужно, но я полагаю, что я что-то не так делаю правильно. Я использую Sun JDK 1.6.18. Любая помощь будет принята с благодарностью.

Ответы [ 2 ]

4 голосов
/ 12 мая 2010

Пока вы выполняете процесс извлечения на EDT, он заблокирует все обновления графического интерфейса, даже монитор прогресса. Это как раз та ситуация, в которой SwingWorker поможет.

По сути, вы блокируете рисование путем включения EDT для извлечения базы данных. Любые запросы на обновление GUI (такие как вызовы repaint()) будут поставлены в очередь, но на самом деле перерисовка этих обновлений никогда не запускается, поскольку EDT уже занят.

Единственный способ, которым это может работать - это перегрузить обработку в другой поток. SwingWorker делает это проще.

2 голосов
/ 12 мая 2010

@ Эш прав насчет SwingWorker. Вы можете использовать done(), чтобы включить другие функции графического интерфейса, когда будете готовы. Метод done() будет «выполнен в потоке диспетчеризации событий после завершения метода doInBackground()».

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...