How to unblock InputStream.read() on Android?
Solution 1
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.
Solution 2
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
).
Solution 3
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
Solution 4
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;
}
Solution 5
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.
tajmahal
Updated on June 12, 2022Comments
-
tajmahal almost 2 years
I have a thread in which the
read()
method of anInputStream
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 theInputStream
from a different thread, the stream gets closed, but the blockedread()
call still remains blocked. I would assume that theread()
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 blockingread()
call corresponds to isclose()
'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.
-
Smudge202 almost 13 yearsI'm not a java guy, but if you control the app/thread sending down the stream, couldn't you send an End-of-message byte/couple of bytes which the reader can use to determine that the stream should be closed?
-
-
Vishy almost 13 yearsavailable() will only tell you if data is available without making a system call. This can return 0 when there is data to read.
-
tajmahal almost 13 yearsOk. That is exactly what I previously had. It is using a lot of cpu, though, as the loop is constantly spinning. I figured that using blocking I/O would remedy this problem.
-
tajmahal almost 13 yearsThat is what I thought. But I do not get an exception. The read() keeps blocking even though the socket is closed.
-
tajmahal almost 13 yearsThank you for the nice code sample! The sample is working fine when compiling it as a regular Java application. Putting it into a bare-bones Android application reveals that the exception is apparently not thrown, even though the very same java.net classes are used. So the problem lies within Android, I assume. Any idea?
-
Vishy almost 13 yearsI have taken the liberty of adding Android as a tag as this appears to be a key detail. ;)
-
Pedro Loureiro almost 13 yearsyou can add a
sleep
as the last line of your while loop. This will prevent you from busy waiting -
tajmahal almost 13 yearsThis solution is not the most elegant one, but the only one that I got to work properly on Android.
-
Tom about 12 yearsNote that the Android docs for available() start with the warning "Note that this method provides such a weak guarantee that it is not very useful in practice."