How to unblock InputStream.read() on Android?
I have a thread in which the read()
method of an InputStream
is called in a loop. When there are no more bytes to read, the stream will block until new data arrives.
If I call close()
on the Input开发者_StackOverflow社区Stream
from a different thread, the stream gets closed, but the blocked read()
call still remains blocked. I would assume that the read()
method should now return with a value of -1
to indicate the end of the stream, but it does not. Instead, it stays blocked for several more minutes until a tcp timeout occurs.
How do I unblock the close()
call?
Edit:
Apparently, the regular JRE will throw a SocketException
immediately when the stream or socket the blocking read()
call corresponds to is close()
'd. The Android Java runtime which I am using, however, will not.
Any hints on a solution for the Android environment would be greatly appreciated.
Only call read()
when there is data available.
Do something like that:
while( flagBlock )
{
if( stream.available() > 0 )
{
stream.read( byteArray );
}
}
set the flagBlock
to stop the reading.
See Java Concurrency In Practice for a really good system to cancel a thread when working with sockets. It uses a special executor (CancellingExecutor
) and a special Callable (SocketUsingTask
).
When the other end closes the connection your stream will return -1 on a read(). If you cannot trigger the other end to close the connection e.g. by closing your output stream, you can close the socket which will cause an IOException in the blocking read() thread.
Can you provide a short example which reproduces your problem?
ServerSocket ss = new ServerSocket(0);
final Socket client = new Socket("localhost", ss.getLocalPort());
Socket server = ss.accept();
Thread t = new Thread(new Runnable() {
public void run() {
int ch;
try {
while ((ch = client.getInputStream().read()) != -1)
System.out.println(ch);
} catch (SocketException se) {
System.out.println(se);
} catch (IOException e) {
e.printStackTrace();
}
}
});
t.start();
server.getOutputStream().write("hi\n".getBytes());
Thread.sleep(100);
client.close();
t.join();
server.close();
ss.close();
prints
104
105
10
java.net.SocketException: Socket closed
We were having the same issue: no exception when switching network (e.g. switching from 3G to WiFi while downloading).
We are using the code from http://www.androidsnippets.com/download-an-http-file-to-sdcard-with-progress-notification, which is working perfectly except in some cases when the network connection was lost.
The solution was specifying a timeout value, this is set standard to 0 (meaning: wait infinitely).
HttpURLConnection c = (HttpURLConnection) u.openConnection();
c.setRequestMethod("GET");
c.setDoOutput(true);
c.setReadTimeout(1000);
c.connect();
Experiment with a timeout value appropriate for you.
I had such issue on Samsung 2.3. When switching from 3G to Wifi InputStream.read() method blocks. I tried all tips from this topic. Nothing helped. From my prospective this is device specific issue because it should throw IOException due to javadoc. My solution is to listen for android broadcast android.net.conn.CONNECTIVITY_CHANGE and close connection from another thread it will cause IOException in blocked thread.
Here is code example:
DownloadThread.java
private volatile boolean canceled;
private volatile InputStream in;
private boolean downloadFile(final File file, final URL url, long totalSize) {
OutputStream out = null;
try {
Log.v(Common.TAG, "DownloadThread: downloading to " + file);
in = (InputStream) url.getContent();
out = new FileOutputStream(file);
return copy(out, totalSize);
} catch (Exception e) {
Log.e(Common.TAG, "DownloadThread: Exception while downloading. Returning false ", e);
return false;
} finally {
closeStream(in);
closeStream(out);
}
}
public void cancelDownloading() {
Log.e(Common.TAG, "DownloadThread: cancelDownloading ");
canceled = true;
closeStream(in); //on my device this is the only way to unblock thread
}
private boolean copy(final OutputStream out, long totalSize) throws IOException {
final int BUFFER_LENGTH = 1024;
final byte[] buffer = new byte[BUFFER_LENGTH];
long totalRead = 0;
int progress = 0;
int read;
while (!canceled && (read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
totalRead += read;
}
return !canceled;
}
You could use java.nio
package. NIO stands for Non-blocking IO. Here the calls (to say read & write) aren't blocked. This way you can close the stream.
There is a sample program you can look at here. Method: processRead
精彩评论