1 package com.bumptech.glide.util; 2 3 import com.bumptech.glide.load.resource.bitmap.RecyclableBufferedInputStream; 4 5 import java.io.IOException; 6 import java.io.InputStream; 7 import java.util.Queue; 8 9 /** 10 * An {@link java.io.InputStream} that catches {@link java.io.IOException}s during read and skip calls and stores them 11 * so they can later be handled or thrown. This class is a workaround for a framework issue where exceptions during 12 * reads while decoding bitmaps in {@link android.graphics.BitmapFactory} can return partially decoded bitmaps. 13 * 14 * See https://github.com/bumptech/glide/issues/126. 15 */ 16 public class ExceptionCatchingInputStream extends InputStream { 17 18 private static final Queue<ExceptionCatchingInputStream> QUEUE = Util.createQueue(0); 19 20 private RecyclableBufferedInputStream wrapped; 21 private IOException exception; 22 obtain(RecyclableBufferedInputStream toWrap)23 public static ExceptionCatchingInputStream obtain(RecyclableBufferedInputStream toWrap) { 24 ExceptionCatchingInputStream result; 25 synchronized (QUEUE) { 26 result = QUEUE.poll(); 27 } 28 if (result == null) { 29 result = new ExceptionCatchingInputStream(); 30 } 31 result.setInputStream(toWrap); 32 return result; 33 } 34 35 // Exposed for testing. clearQueue()36 static void clearQueue() { 37 while (!QUEUE.isEmpty()) { 38 QUEUE.remove(); 39 } 40 } 41 ExceptionCatchingInputStream()42 ExceptionCatchingInputStream() { 43 // Do nothing. 44 } 45 setInputStream(RecyclableBufferedInputStream toWrap)46 void setInputStream(RecyclableBufferedInputStream toWrap) { 47 wrapped = toWrap; 48 } 49 50 @Override available()51 public int available() throws IOException { 52 return wrapped.available(); 53 } 54 55 @Override close()56 public void close() throws IOException { 57 wrapped.close(); 58 } 59 60 @Override mark(int readlimit)61 public void mark(int readlimit) { 62 wrapped.mark(readlimit); 63 } 64 65 @Override markSupported()66 public boolean markSupported() { 67 return wrapped.markSupported(); 68 } 69 70 @Override read(byte[] buffer)71 public int read(byte[] buffer) throws IOException { 72 int read; 73 try { 74 read = wrapped.read(buffer); 75 } catch (IOException e) { 76 exception = e; 77 read = -1; 78 } 79 return read; 80 } 81 82 @Override read(byte[] buffer, int byteOffset, int byteCount)83 public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException { 84 int read; 85 try { 86 read = wrapped.read(buffer, byteOffset, byteCount); 87 } catch (IOException e) { 88 exception = e; 89 read = -1; 90 } 91 return read; 92 } 93 94 @Override reset()95 public synchronized void reset() throws IOException { 96 wrapped.reset(); 97 } 98 99 @Override skip(long byteCount)100 public long skip(long byteCount) throws IOException { 101 long skipped; 102 try { 103 skipped = wrapped.skip(byteCount); 104 } catch (IOException e) { 105 exception = e; 106 skipped = 0; 107 } 108 return skipped; 109 } 110 111 @Override read()112 public int read() throws IOException { 113 int result; 114 try { 115 result = wrapped.read(); 116 } catch (IOException e) { 117 exception = e; 118 result = -1; 119 } 120 return result; 121 } 122 fixMarkLimit()123 public void fixMarkLimit() { 124 wrapped.fixMarkLimit(); 125 } 126 getException()127 public IOException getException() { 128 return exception; 129 } 130 release()131 public void release() { 132 exception = null; 133 wrapped = null; 134 synchronized (QUEUE) { 135 QUEUE.offer(this); 136 } 137 } 138 } 139