我正在使用以下方法从服务器下载图像以在Android应用程序中提供服务。
public static void downloadToFile(File file, URL url, int connectTimeout,
int readTimeout) throws IOException {
InputStream in = null;
OutputStream out = null;
try {
URLConnection ucon = url.openConnection();
ucon.setConnectTimeout(connectTimeout);
ucon.setReadTimeout(readTimeout);
in = ucon.getInputStream();
out = new FileOutputStream(file);
byte[] buffer = new byte[BUFFER_SIZE];
int count = 0;
while ((count = in.read(buffer)) != -1) {
out.write(buffer, 0, count);
}
out.flush();
} finally {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
}
}
当一个文件已经从服务器上删除,但我试图下载它,我收到一个FileNotFoundException,这是完全有效的。在我的应用程序中,对这个方法的调用被包装在一个try catch中,处理IOException和Exception,所以我可以继续。
问题是,在Android开发者控制台我收到了一些崩溃报告和FileNotFoundException似乎忽略了catch块和强制关闭应用程序。
java.io.FileNotFoundException: http://foo.org/1234.jpg
at org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:1162)
at com.package.IOUtil.downloadToFile(Unknown Source)
未知源是由于Proguard,但我可以在测试时通过放入不存在的文件url来强制异常,这确实会抛出异常。当我在手机(SGS2 2.3.3)和平板电脑(TF101 3.2)上测试时,try catch块确实捕获异常。下面的堆栈跟踪指向URL#getInputStream()作为抛出异常的原因,但这并不能解释为什么它没有被某些(只有少数)捕获。
java.io.FileNotFoundException: http://foo.org/1234.jpg
at org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:521)
at com.package.IOUtil.downloadToFile(IOUtil.java:142)
我也遇到了同样的问题,来自相同方法/调用者的SocketException没有被捕获。
java.net.SocketTimeoutException: Connection timed out
at org.apache.harmony.luni.platform.OSNetworkSystem.connect(Native Method)
at dalvik.system.BlockGuard$WrappedNetworkSystem.connect(BlockGuard.java:357)
at org.apache.harmony.luni.net.PlainSocketImpl.connect(PlainSocketImpl.java:204)
at org.apache.harmony.luni.net.PlainSocketImpl.connect(PlainSocketImpl.java:437)
at java.net.Socket.connect(Socket.java:1002)
at org.apache.harmony.luni.internal.net.www.protocol.http.HttpConnection.<init>(HttpConnection.java:75)
at org.apache.harmony.luni.internal.net.www.protocol.http.HttpConnection.<init>(HttpConnection.java:48)
at org.apache.harmony.luni.internal.net.www.protocol.http.HttpConnection$Address.connect(HttpConnection.java:322)
at org.apache.harmony.luni.internal.net.www.protocol.http.HttpConnectionPool.get(HttpConnectionPool.java:89)
at org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.getHttpConnection(HttpURLConnectionImpl.java:285)
at org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.makeConnection(HttpURLConnectionImpl.java:267)
at org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.retrieveResponse(HttpURLConnectionImpl.java:1018)
at org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:512)
at com.package.IOUtil.downloadToFile(Unknown Source)
如果需要的话,下载在后台线程中完成。
我错过了什么吗?它可能是特定于手机的,或者是Proguard(尽管几乎所有人都发现了它)?
编辑:捕获 的代码private void download(List<URL> urls, int attempts) {
List<URL> failedDownloads = new ArrayList<URL>();
for (URL url : urls) {
if (isInterrupted()) {
return;
}
String imageName = Util.getImageName(url);
File cacheFile = cache.getCacheFile(imageName);
try {
// Bad downloads often don't throw an IOException but
// leave the file with a length of 0.
if (cacheFile.length() == 0) {
Util.safeDelete(cacheFile);
}
if (!cacheFile.exists()) {
IOUtil.downloadToFile(cacheFile, url);
if (cacheFile.length() == 0) {
//See above
Util.safeDelete(cacheFile);
failedDownloads.add(url);
} else {
if (putToCache) {
cache.get(imageName);
}
handler.sendEmptyMessage(0);
}
} else {
// Exists already, move it to cache
if (putToCache) {
cache.get(imageName);
}
}
} catch (IOException ioe) {
Util.safeDelete(cacheFile);
failedDownloads.add(url);
} catch (Exception e) {
// Continue on to next download - don't bother trying this
// URL again.
}
}
if (!failedDownloads.isEmpty() && attempts < maxAttempts) {
attempts++;
download(failedDownloads, attempts);
}
}
跑龙套。safeDelete只是一个在try catch中封装的delete操作,所以它们之间不应该有关联。
首先,不要抓住并压制Exception
。别这样。不。如果出现了意想不到的问题,你就是在抛弃所有的证据。
其次,重新编译包含调试信息的代码,您将在堆栈跟踪中获得方法的文件名和行号。在开发/测试过程中摆脱Proguard。(最好完全摆脱它,因为它不能给你任何真正的保护。)
最后,download
方法(因为您已经复制了它)不可能导致未捕获的IOException
。如果是:
- 你清楚地捕获异常,并且
- 会有一个编译错误的方法,因为它没有声明
IOException
,和 -
download
方法将是堆栈跟踪的一部分,而
因此它一定是类似于…
- 您已经声明并导入了另一个
IOException
类到该文件中,或者 罪魁祸首是你的 -
download
方法重载,异常发生在不同的重载中,或者 - 您正在调用
download
之外的downloadToFile
,或者 - 您复制的代码不准确,或者
- 你在编译/部署代码时犯了错误;例如:编辑后忘记重新编译
safeDelete
方法,或者如果需要的话,下载在后台线程中完成。
是的,这很重要。该异常在后台线程中抛出,并有效地消失在稀薄的空气中。
我认为解决方案可以在这里找到:如何抛出一个检查异常从java线程?把后台线程放到Callable中可能是最好的方法
欢呼
奇怪的是,catch异常没有捕获FileNotFoundException。看来你已经在下载方法中捕获了异常。
你可以尝试在downloadToFile中捕捉异常,看看会发生什么。
解决方案(在我的案例中)
当服务器响应代码>= HTTP_BAD_REQUEST(大于400)时,In case 方法gettinputstream(),类HttpURLConnectionImpl抛出FileNotFoundException(所以你不能打开输入流).
即使这个文件存在,你的对象也不会给你输入流,因为服务器的响应代码是>=400 - 更改服务器的响应代码或使用另一个类来连接。
09-07 16:08:08.749: WARN/System.err(360): at org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:1162)
源代码片段:http://www.docjar.com/html/api/org/apache/harmony/luni/internal/net/www/protocol/http/HttpURLConnectionImpl.java.html
867 @Override
868 public InputStream getInputStream() throws IOException {
869 if (!doInput) {
870 throw new ProtocolException(Messages.getString("luni.28")); //$NON-NLS-1$
871 }
872
873 // connect before sending requests
874 connect();
875 doRequest();
876
...
883 if (responseCode >= HTTP_BAD_REQUEST) {
884 throw new FileNotFoundException(url.toString());
885 }
886
887 return uis;
888 }