我正在开发一个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方法的帖子中找到问题的可能原因而没有成功。
任何帮助表示赞赏。谢谢
答案 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;
}