blob: 344a2894f94fa4c0492b32c7666e25cc422628bc [file] [log] [blame]
package com.bumptech.glide.util;
import com.bumptech.glide.load.resource.bitmap.RecyclableBufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Queue;
/**
* An {@link java.io.InputStream} that catches {@link java.io.IOException}s during read and skip calls and stores them
* so they can later be handled or thrown. This class is a workaround for a framework issue where exceptions during
* reads while decoding bitmaps in {@link android.graphics.BitmapFactory} can return partially decoded bitmaps.
*
* See https://github.com/bumptech/glide/issues/126.
*/
public class ExceptionCatchingInputStream extends InputStream {
private static final Queue<ExceptionCatchingInputStream> QUEUE = Util.createQueue(0);
private RecyclableBufferedInputStream wrapped;
private IOException exception;
public static ExceptionCatchingInputStream obtain(RecyclableBufferedInputStream toWrap) {
ExceptionCatchingInputStream result;
synchronized (QUEUE) {
result = QUEUE.poll();
}
if (result == null) {
result = new ExceptionCatchingInputStream();
}
result.setInputStream(toWrap);
return result;
}
// Exposed for testing.
static void clearQueue() {
while (!QUEUE.isEmpty()) {
QUEUE.remove();
}
}
ExceptionCatchingInputStream() {
// Do nothing.
}
void setInputStream(RecyclableBufferedInputStream toWrap) {
wrapped = toWrap;
}
@Override
public int available() throws IOException {
return wrapped.available();
}
@Override
public void close() throws IOException {
wrapped.close();
}
@Override
public void mark(int readlimit) {
wrapped.mark(readlimit);
}
@Override
public boolean markSupported() {
return wrapped.markSupported();
}
@Override
public int read(byte[] buffer) throws IOException {
int read;
try {
read = wrapped.read(buffer);
} catch (IOException e) {
exception = e;
read = -1;
}
return read;
}
@Override
public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
int read;
try {
read = wrapped.read(buffer, byteOffset, byteCount);
} catch (IOException e) {
exception = e;
read = -1;
}
return read;
}
@Override
public synchronized void reset() throws IOException {
wrapped.reset();
}
@Override
public long skip(long byteCount) throws IOException {
long skipped;
try {
skipped = wrapped.skip(byteCount);
} catch (IOException e) {
exception = e;
skipped = 0;
}
return skipped;
}
@Override
public int read() throws IOException {
int result;
try {
result = wrapped.read();
} catch (IOException e) {
exception = e;
result = -1;
}
return result;
}
public void fixMarkLimit() {
wrapped.fixMarkLimit();
}
public IOException getException() {
return exception;
}
public void release() {
exception = null;
wrapped = null;
synchronized (QUEUE) {
QUEUE.offer(this);
}
}
}