29
29
import android .util .Log ;
30
30
31
31
import java .io .File ;
32
+ import java .io .FileInputStream ;
33
+ import java .io .FileNotFoundException ;
32
34
import java .io .IOException ;
33
35
import java .io .InputStream ;
34
36
import java .io .OutputStream ;
45
47
*
46
48
* <p> Instances of this class should ideally be kept globally with the application, for example in
47
49
* the {@link android.app.Application Application} object. You should also use the bundled {@link
48
- * CacheableImageView} wherever possible, as the memory cache has a close relationship with it.
50
+ * CacheableImageView} wherever possible, as the memory cache has a closeStream relationship with it.
49
51
* </p>
50
52
*
51
53
* <p> Clients can call {@link #get(String)} to retrieve a cached value from the given Url. This
@@ -84,6 +86,15 @@ public static enum RecyclePolicy {
84
86
*/
85
87
ALWAYS ;
86
88
89
+ boolean canInBitmap () {
90
+ switch (this ) {
91
+ case PRE_HONEYCOMB_ONLY :
92
+ case DISABLED :
93
+ return Build .VERSION .SDK_INT >= Build .VERSION_CODES .HONEYCOMB ;
94
+ }
95
+ return false ;
96
+ }
97
+
87
98
boolean canRecycle () {
88
99
switch (this ) {
89
100
case DISABLED :
@@ -257,24 +268,20 @@ public CacheableBitmapDrawable getFromDiskCache(final String url,
257
268
258
269
try {
259
270
final String key = transformUrlForDiskCacheKey (url );
260
- DiskLruCache .Snapshot snapshot = mDiskCache .get (key );
261
- if (null != snapshot ) {
262
- // Try and decode bitmap
263
- Bitmap bitmap = BitmapFactory
264
- .decodeStream (snapshot .getInputStream (0 ), null , decodeOpts );
265
-
266
- if (null != bitmap ) {
267
- result = new CacheableBitmapDrawable (url , mResources , bitmap ,
268
- mRecyclePolicy );
269
- if (null != mMemoryCache ) {
270
- mMemoryCache .put (result );
271
- }
272
- } else {
273
- // If we get here, the file in the cache can't be
274
- // decoded. Remove it and schedule a flush.
275
- mDiskCache .remove (key );
276
- scheduleDiskCacheFlush ();
271
+ // Try and decode bitmap
272
+ Bitmap bitmap = decodeBitmap (new SnapshotInputStreamProvider (key ), decodeOpts );
273
+
274
+ if (null != bitmap ) {
275
+ result = new CacheableBitmapDrawable (url , mResources , bitmap ,
276
+ mRecyclePolicy );
277
+ if (null != mMemoryCache ) {
278
+ mMemoryCache .put (result );
277
279
}
280
+ } else {
281
+ // If we get here, the file in the cache can't be
282
+ // decoded. Remove it and schedule a flush.
283
+ mDiskCache .remove (key );
284
+ scheduleDiskCacheFlush ();
278
285
}
279
286
} catch (IOException e ) {
280
287
e .printStackTrace ();
@@ -301,7 +308,7 @@ public CacheableBitmapDrawable getFromMemoryCache(final String url) {
301
308
result = mMemoryCache .get (url );
302
309
303
310
// If we get a value, but it has a invalid bitmap, remove it
304
- if (null != result && !result .hasValidBitmap ()) {
311
+ if (null != result && !result .isBitmapValid ()) {
305
312
mMemoryCache .remove (url );
306
313
result = null ;
307
314
}
@@ -379,14 +386,7 @@ public CacheableBitmapDrawable put(final String url, final Bitmap bitmap,
379
386
} catch (IOException e ) {
380
387
Log .e (Constants .LOG_TAG , "Error while writing to disk cache" , e );
381
388
} finally {
382
- if (null != os ) {
383
- try {
384
- os .close ();
385
- } catch (IOException e ) {
386
- Log .e (Constants .LOG_TAG , "Failed to close output stream" , e );
387
- }
388
- }
389
-
389
+ IoUtils .closeStream (os );
390
390
lock .unlock ();
391
391
scheduleDiskCacheFlush ();
392
392
}
@@ -448,7 +448,7 @@ public CacheableBitmapDrawable put(final String url, final InputStream inputStre
448
448
449
449
if (null != tmpFile ) {
450
450
// Try and decode File
451
- Bitmap bitmap = BitmapFactory . decodeFile ( tmpFile . getAbsolutePath ( ), decodeOpts );
451
+ Bitmap bitmap = decodeBitmap ( new FileInputStreamProvider ( tmpFile ), decodeOpts );
452
452
453
453
if (null != bitmap ) {
454
454
d = new CacheableBitmapDrawable (url , mResources , bitmap , mRecyclePolicy );
@@ -525,9 +525,9 @@ synchronized void setDiskCache(DiskLruCache diskCache) {
525
525
}
526
526
}
527
527
528
- void setMemoryCache (BitmapMemoryLruCache memoryCache , RecyclePolicy recyclePolicy ) {
528
+ void setMemoryCache (BitmapMemoryLruCache memoryCache ) {
529
529
mMemoryCache = memoryCache ;
530
- mRecyclePolicy = recyclePolicy ;
530
+ mRecyclePolicy = memoryCache . getRecyclePolicy () ;
531
531
}
532
532
533
533
private ReentrantLock getLockForDiskCacheEdit (String url ) {
@@ -553,6 +553,58 @@ private void scheduleDiskCacheFlush() {
553
553
TimeUnit .SECONDS );
554
554
}
555
555
556
+ private Bitmap decodeBitmap (InputStreamProvider ip , BitmapFactory .Options opts ) {
557
+ Bitmap bm = null ;
558
+ InputStream is = null ;
559
+
560
+ try {
561
+ if (mRecyclePolicy .canInBitmap ()) {
562
+ // Create an options instance if we haven't been provided with one
563
+ if (opts == null ) {
564
+ opts = new BitmapFactory .Options ();
565
+ }
566
+
567
+ if (opts .inSampleSize <= 1 ) {
568
+ opts .inSampleSize = 1 ;
569
+ addInBitmapOptions (ip , opts );
570
+ }
571
+ }
572
+
573
+ // Get InputStream for actual decode
574
+ is = ip .getInputStream ();
575
+ // Decode stream
576
+ bm = BitmapFactory .decodeStream (is , null , opts );
577
+ } catch (Exception e ) {
578
+ Log .e (Constants .LOG_TAG , "Unable to decode stream" , e );
579
+ } finally {
580
+ IoUtils .closeStream (is );
581
+ }
582
+ return bm ;
583
+ }
584
+
585
+ private void addInBitmapOptions (InputStreamProvider ip , BitmapFactory .Options opts ) {
586
+ // Create InputStream for decoding the bounds
587
+ final InputStream is = ip .getInputStream ();
588
+ // Decode the bounds so we know what size Bitmap to look for
589
+ opts .inJustDecodeBounds = true ;
590
+ BitmapFactory .decodeStream (is , null , opts );
591
+ IoUtils .closeStream (is );
592
+
593
+ // Turn off just decoding bounds
594
+ opts .inJustDecodeBounds = false ;
595
+ // Make sure the decoded file is mutable
596
+ opts .inMutable = true ;
597
+
598
+ // Try and find Bitmap to use for inBitmap
599
+ Bitmap reusableBm = mMemoryCache .getBitmapFromRemoved (opts .outWidth , opts .outHeight );
600
+ if (reusableBm != null ) {
601
+ if (Constants .DEBUG ) {
602
+ Log .i (Constants .LOG_TAG , "Using inBitmap" );
603
+ }
604
+ SDK11 .addInBitmapOption (opts , reusableBm );
605
+ }
606
+ }
607
+
556
608
/**
557
609
* Builder class for {link {@link BitmapLruCache}. An example call:
558
610
*
@@ -578,7 +630,7 @@ public final static class Builder {
578
630
579
631
static final int DEFAULT_MEM_CACHE_MAX_SIZE_MB = 3 ;
580
632
581
- static final RecyclePolicy DEFAULT_RECYCLE_POLICY = RecyclePolicy .ALWAYS ;
633
+ static final RecyclePolicy DEFAULT_RECYCLE_POLICY = RecyclePolicy .PRE_HONEYCOMB_ONLY ;
582
634
583
635
// Only used for Javadoc
584
636
static final float DEFAULT_MEMORY_CACHE_HEAP_PERCENTAGE = DEFAULT_MEMORY_CACHE_HEAP_RATIO
@@ -635,7 +687,7 @@ public BitmapLruCache build() {
635
687
if (Constants .DEBUG ) {
636
688
Log .d ("BitmapLruCache.Builder" , "Creating Memory Cache" );
637
689
}
638
- cache .setMemoryCache (new BitmapMemoryLruCache (mMemoryCacheMaxSize ) , mRecyclePolicy );
690
+ cache .setMemoryCache (new BitmapMemoryLruCache (mMemoryCacheMaxSize , mRecyclePolicy ) );
639
691
}
640
692
641
693
if (isValidOptionsForDiskCache ()) {
@@ -799,4 +851,47 @@ public void run() {
799
851
}
800
852
}
801
853
}
854
+
855
+ interface InputStreamProvider {
856
+ InputStream getInputStream ();
857
+ }
858
+
859
+ static class FileInputStreamProvider implements InputStreamProvider {
860
+ final File mFile ;
861
+
862
+ FileInputStreamProvider (File file ) {
863
+ mFile = file ;
864
+ }
865
+
866
+ @ Override
867
+ public InputStream getInputStream () {
868
+ try {
869
+ return new FileInputStream (mFile );
870
+ } catch (FileNotFoundException e ) {
871
+ Log .e (Constants .LOG_TAG , "Could not decode file: " + mFile .getAbsolutePath (), e );
872
+ }
873
+ return null ;
874
+ }
875
+ }
876
+
877
+ final class SnapshotInputStreamProvider implements InputStreamProvider {
878
+ final String mKey ;
879
+
880
+ SnapshotInputStreamProvider (String key ) {
881
+ mKey = key ;
882
+ }
883
+
884
+ @ Override
885
+ public InputStream getInputStream () {
886
+ try {
887
+ DiskLruCache .Snapshot snapshot = mDiskCache .get (mKey );
888
+ if (snapshot != null ) {
889
+ return snapshot .getInputStream (0 );
890
+ }
891
+ } catch (IOException e ) {
892
+ Log .e (Constants .LOG_TAG , "Could open disk cache for url: " + mKey , e );
893
+ }
894
+ return null ;
895
+ }
896
+ }
802
897
}
0 commit comments