简体   繁体   中英

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 InputStream 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.

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 eg 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

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 ).

We were having the same issue: no exception when switching network (eg 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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM