I'm using the following method to download images from a server to serve in an Android application.
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();
}
}
}
When a file has been deleted from the server but I try to download it anyway, I receive a FileNotFoundException, which is perfectly valid. In my application, the call to this method is wrapped in a try catch handling both IOException and Exception so I can just continue on.
The problem is that in the Android Developer Console I've received a few crash reports and the FileNotFoundException seems to be ignoring the catch blocks and force closing the app.
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)
The Unknown Source is because of Proguard but I can force the exception when testing by putting in non-existant file URLS which does throw the exception. When I test on my phone (SGS2 2.3.3) and tablet (TF101 3.2) the try catch block does catch the exception. The stack trace below points to the URL#getInputStream() as what is throwing the exception but that doesn't explain why it isn't being caught for some (only a few).
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)
I've also had the same problem with a SocketException from the same method/callee not being caught.
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)
The downloading is done in a background thread if that matters.
Am I missing something? Could it be phone specific, or Proguard (even though it's caught by almost everybody else)?
Edit: Code that catches
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);
}
}
Util.safeDelete is just a delete wrapped in a try catch so that shouldn't be related.