Java 解压png文件时有时会出现线程阻塞
我正在开发一个android应用程序,需要从Web服务器下载一个带有少量徽标(平均大小为20-30KB的png文件)的zip文件(最大约1.5MB) 我已经在Java 解压png文件时有时会出现线程阻塞,java,android,Java,Android,我正在开发一个android应用程序,需要从Web服务器下载一个带有少量徽标(平均大小为20-30KB的png文件)的zip文件(最大约1.5MB) 我已经在AsyncTask的doInbackground()方法中将下载和解压缩文件的过程封装到android内部存储中。 我遇到的问题是,我开发的unZipIntoInternalStorage()方法(粘贴下来),有时会永远运行下去。通常,解压徽标并将其保存到内部存储器大约需要900毫秒的时间,但由于某些未知原因,在循环过程中,大约有4个执行块
AsyncTask
的doInbackground()
方法中将下载和解压缩文件的过程封装到android内部存储中。我遇到的问题是,我开发的
unZipIntoInternalStorage()
方法(粘贴下来),有时会永远运行下去。通常,解压徽标并将其保存到内部存储器大约需要900毫秒的时间,但由于某些未知原因,在循环过程中,大约有4个执行块中的1个执行块(并且“永远”停留在那里,解压所有png文件需要2或3分钟以上):
已编辑:在完成一些日志记录和调试之后,我发现执行速度太慢,执行速度太慢了:zipInputStream.read(buffer)
在while条件内。你知道为什么有时候它跑得非常快,而有些却非常慢吗
下面是我解压下载文件并将其保存到android内部存储的完整方法。我还添加了从下载的zip文件初始化zipInputStream
的方法(这两种方法都在doInBackground()中执行)
:
为了澄清这一点,在多次测试该方法并进行调试之后,我前面描述的嵌套while循环中始终会发生阻塞。但我找不到原因(见编辑的澄清)
另外,我已经使用BufferedOutputStream
类尝试了这种方法,结果是相同的:嵌套的while循环有时永远运行,而其他循环在不到一秒钟的时间内成功解压
希望我已经尽可能清楚了,因为我已经花了很长时间在几篇关于解压缩文件或java I/O方法的文章中寻找问题的可能原因,但都没有成功
谢谢你的帮助。谢谢我怀疑问题出在输入流而不是输出 尝试:
返回新的ZipInputStream(新的BufferedInputStream(urlConnection.getInputStream())代码>
您可以添加一个参数来设置缓冲区大小,但是默认设置应该适合您的用例
该问题通常是由较小的数据包大小引起的,导致一次读取强制执行多个IO操作
理想情况下,您确实希望同时使用BufferedOutputStream,因为读取
可以读取远小于1kB的数据,但您仍然需要为每次写入支付完整的I/O
作为一般规则,请记住I/O的速度比您可以执行的任何其他操作都慢100倍,并且通常会导致调度程序将您的任务置于等待状态。因此,只要在流不在内存中的任何位置使用BufferedStream(即,基本上总是StringBufferXXXStream除外)
在您的情况下,由于zip协议,当zip解析和解释压缩文件的标题和内容时,读取
可能会导致实际网络套接字上的任何数量的较小读取 我知道这个错误。如果您的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;
}
我相信您遇到了在内部while
循环中找不到的“流结束”(并且您指出内部while循环似乎是导致问题的原因)。按原样,内部while循环只有在接收到“流结束”时才会退出,但我必须承认,我不知道“流结束”标记是什么,只是我也遇到过类似的问题。那么,如何解决没有找到流结束的问题呢?在这种情况下,是否有任何代码可以中止解压?是否可以记录或打印每次迭代的计数值?这将为您提供一条线索,read()
方法是否永久阻塞,或者write()
方法是否永久阻塞。如果count==0
(根据InputStream的规范,这种情况永远不会发生)可能会发生有趣的问题。在ZipInputStream
中包装了什么类型的InputStream
?它是文件输入流还是来自套接字的输入流?在后一种情况下,网络可能会出现问题。而且,由于解压是在一个单独的线程上执行的,您确定解压过程中实际创建异步任务的线程没有关闭ZipInputStream
吗?可能urlConnection.setReadTimeout(ms)代码>需要吗?1 KiB的缓冲区也有点小。我尝试了你的建议,将urlConnection.getInputStream()包装在BufferedOutputStream中,并将FileOutputStream包装在BufferedOutputStream中,但仍然存在执行时间过长的问题。那么可能与网络插座有关吗?我以为zipInputStream返回的那一刻,网络操作就结束了。不管怎样,谢谢你的建议。我相信Zip流在工作时(即,它是一个流)会解码条目,而不是在协同构造时。由于缓冲没有帮助,你正在访问的服务器可能是罪魁祸首,当你从同一个源IP快速访问大量url时,可能会给你低优先级。我认为要进行调试,我应该将从url获取文件内容(完整)与解码分开,例如,通过中间步骤,在打开ZipStream之前复制到ByteArrayOutputStream。然后在执行操作时,您可以诊断哪些文件需要长时间下载,以及您的性能问题是否来自此下载步骤/网络。显然,您是对的!问题似乎来自服务器。在我们在Amazon上使用web服务器(带有nginx服务器的EC2实例)之前,最近我们已经迁移到Amazon cloudfront来为
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;
}
}
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;
}