解压缩png文件时有时会阻塞线程

时间:2015-07-01 09:21:49

标签: java android

我正在开发一个Android应用程序,需要从网络服务器下载一个zip文件(最大约1.5 MB)和少量徽标(平均大小为20-30KB的png文件)。

我已经使用AsyncTask doInbackground()方法封装了将文件下载并解压缩到Android内部存储空间的过程。
我遇到的问题是我开发的unZipIntoInternalStorage()方法(粘贴),有时永远运行。解压缩并将徽标保存到内部存储器通常需要大约900毫秒的时间,但由于某些未知原因,循环期间大约有4个执行块中的一个(并且停留在那里"永远"需要超过2或3分钟解压缩所有png文件):

while ((count = zipInputStream.read(buffer)) != -1) {
         outputStream.write(buffer, 0, count);
}

已编辑:执行了一些日志记录和调试后,我发现该行的执行速度减慢了很多:zipInputStream.read(buffer)在while条件下。任何想法为什么有时它运行速度极快而其他一些非常慢?

这是我解压缩下载文件并将其保存到android内部存储的完整方法。我还从下载的zip文件(在zipInputStream内执行的两个方法)中添加了doInBackground()初始化的方法:

        private void unZipIntoInternalStorage(ZipInputStream zipInputStream) {
        long start = System.currentTimeMillis();
        Log.i(LOG_TAG, "Unzipping started ");
        try {
            File iconsDir = context.getDir("icons", Context.MODE_PRIVATE);
            ZipEntry zipEntry;
            byte[] buffer = new byte[1024];
            int count;
            FileOutputStream outputStream;

            while ((zipEntry = zipInputStream.getNextEntry()) != null) {
                File icon = new File(iconsDir, zipEntry.getName());
                outputStream = new FileOutputStream(icon);

                while ((count = zipInputStream.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, count);
                }
                zipInputStream.closeEntry();
                outputStream.close();
            }
            zipInputStream.close();
        } catch (Exception e) {
            Log.e(LOG_TAG + " Decompress", "unzip error ", e);
            e.printStackTrace();
        }
        Log.i(LOG_TAG, "Unzipping completed time required: " + (System.currentTimeMillis() - start) + " ms");
    }

    private ZipInputStream httpDownloadIconsZip(String zipUrl) {

        URLConnection urlConnection;
        try {
            URL finalUrl = new URL(zipUrl);
            urlConnection = finalUrl.openConnection();
            return new ZipInputStream(urlConnection.getInputStream());
        } catch (IOException e) {
            Log.e(LOG_TAG, Log.getStackTraceString(e));
            return null;
        }
    }

为了澄清,在多次测试此方法并进行调试之后,阻塞始终发生在我之前描述的嵌套while循环中。但我无法找到原因(见编辑澄清) 此外,我已经尝试使用BufferedOutputStream类并使用相同的结果:嵌套while循环有时永远运行而其他人在不到一秒的时间内成功解压缩。

希望我尽可能地清楚,因为我花了很长时间在几篇关于解压缩文件或java I / O方法的帖子中找到问题的可能原因而没有成功。

任何帮助表示赞赏。谢谢

2 个答案:

答案 0 :(得分:1)

我怀疑InputStream而不是输出是问题。

尝试: return new ZipInputStream(new BufferedInputStream(urlConnection.getInputStream()));

您可以添加一个参数来设置缓冲区大小,但默认设置应该适用于您的用例。

问题通常是由较小的数据包大小引起的,导致一次读取强制执行多个IO操作。

理想情况下,您确实也希望使用BufferedOutputStream,因为read读取的内容远小于1kB,但您仍然需要为每次写入支付完整的I / O.

作为一般规则,记住I / O比你可以做的任何其他事情慢100倍,并且经常导致调度程序将你的任务放在Wait上。所以只要在流不在内存中的任何地方使用BufferedStream(即基本上除了StringBufferXXXStream之外)。

在您的情况下,由于zip协议,您的read可能会导致实际网络套接字上的任意数量的较小读取,因为Zip会解析并解释压缩文件的标头和内容。

答案 1 :(得分:0)

我知道这个错误。如果您的zip文件已损坏,当您尝试按ZipInputStream解压缩时,它将是一个无限循环,因为该文件没有EOF。

但如果您通过ZipFile解压缩它,您可以捕获该异常!

public static boolean unZipByFilePath(String fileName, String unZipDir) {
    long startUnZipTime = System.currentTimeMillis();
    try {
        File f = new File(unZipDir);
        if (!f.exists()) {
            f.mkdirs();
        }

        BufferedOutputStream dest = null;
        BufferedInputStream is = null;
        ZipEntry entry;
        ZipFile zipfile = new ZipFile(fileName);
        Enumeration e = zipfile.entries();
        while (e.hasMoreElements()) {
            entry = (ZipEntry) e.nextElement();
            is = new BufferedInputStream(zipfile.getInputStream(entry));
            int count = 0;
            byte data[] = new byte[BUFFER];

            String destFilePath = unZipDir + "/" + entry.getName();
            File desFile = new File(destFilePath);
            if (entry.isDirectory()) {
                desFile.mkdirs();
            } else if (!desFile.exists()) {
                desFile.getParentFile().mkdirs();
                desFile.createNewFile();
            }

            FileOutputStream fos = new FileOutputStream(destFilePath);
            dest = new BufferedOutputStream(fos, BUFFER);
            while ((count = is.read(data, 0, BUFFER)) != -1) {
                dest.write(data, 0, count);
            }
            dest.flush();
            dest.close();
            is.close();
        }
        zipfile.close();
    } catch (Exception e) {
        Log.e(TAG, "unZipByFilePath failed : " + e.getMessage());
        return false;
    }
    return true;
}