├── .gitignore
├── README.md
├── library
├── AndroidManifest.xml
├── libs
│ ├── android-support-v4.jar
│ └── disklrucache-2.0.1.jar
├── pom.xml
├── project.properties
├── res
│ └── values
│ │ └── strings.xml
└── src
│ └── uk
│ └── co
│ └── senab
│ └── bitmapcache
│ ├── BitmapLruCache.java
│ ├── BitmapMemoryLruCache.java
│ ├── CacheableBitmapDrawable.java
│ ├── CacheableImageView.java
│ ├── Constants.java
│ ├── IoUtils.java
│ ├── Md5.java
│ ├── SDK11.java
│ └── WeakReferenceRunnable.java
├── pom.xml
└── sample
├── AndroidManifest.xml
├── pom.xml
├── project.properties
├── res
├── drawable-hdpi
│ └── ic_launcher.png
├── drawable-mdpi
│ └── ic_launcher.png
├── drawable-xhdpi
│ └── ic_launcher.png
├── layout
│ ├── activity_gridview.xml
│ └── gridview_item_layout.xml
└── values
│ ├── colors.xml
│ ├── dimens.xml
│ └── strings.xml
└── src
└── uk
└── co
└── senab
└── bitmapcache
└── samples
├── GridViewActivity.java
├── LauncherActivity.java
├── NetworkedCacheableImageView.java
├── PugListAdapter.java
├── PugPagerAdapter.java
├── SDK11.java
├── SampleApplication.java
└── ViewPagerActivity.java
/.gitignore:
--------------------------------------------------------------------------------
1 | #Android generated
2 | bin
3 | gen
4 |
5 | #Eclipse
6 | .project
7 | .classpath
8 | .settings
9 |
10 | #IntelliJ IDEA
11 | .idea
12 | *.iml
13 | *.ipr
14 | *.iws
15 | out
16 |
17 | #Maven
18 | target
19 | release.properties
20 | pom.xml.*
21 |
22 | #Ant
23 | build.xml
24 | local.properties
25 | proguard.cfg
26 |
27 | #OSX
28 | .DS_Store
29 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Android-BitmapCache
2 | =========================
3 |
4 | This project came about as part of my blog post: [http://www.senab.co.uk/2012/07/01/android-bitmap-caching-revisited/](http://www.senab.co.uk/2012/07/01/android-bitmap-caching-revisited/)
5 |
6 | Android-BitmapCache is a specialised cache, for use with Android Bitmap objects.
7 |
8 | I have added a sample app to the source since, which can also be downloaded from the Downloads tab above. The sample app shows you how to use the library by creating a ViewPager of images downloaded from the web. These are cached in the LruCache and/or Disk Cache.
9 |
10 | ## Summary
11 |
12 | A cache which can be set to use multiple layers of caching for Bitmap objects
13 | in an Android app. Instances are created via a `BitmapLruCache.Builder` instance,
14 | which can be used to alter the settings of the resulting cache.
15 |
16 | Instances of this class should ideally be kept globally within your application,
17 | for example in the `Application` object.
18 |
19 | If you wish for the library and recycling feature to work, you **MUST** use the bundled [CacheableImageView](https://github.com/chrisbanes/Android-BitmapCache/blob/master/library/src/uk/co/senab/bitmapcache/CacheableImageView.java) wherever possible.
20 |
21 | ## Usage
22 |
23 | Clients can call `get(String)` to retrieve a cached value from the
24 | given Url. This will check all available caches for the value. There are also
25 | the `getFromDiskCache(String)` and `getFromMemoryCache(String)`
26 | which allow more granular access.
27 |
28 | There are a number of update methods. `put(String, InputStream)` and
29 | `put(String, InputStream, boolean)` are the preferred versions of the
30 | method, as they allow 1:1 caching to disk of the original content.
31 | `put(String, Bitmap)` and `put(String, Bitmap, boolean)` should
32 | only be used if you can't get access to the original InputStream.
33 |
34 | ## Obtaining
35 | The easy way to use the library is by downloading the JAR file, and importing it into your Eclipse project. You can find the latest JAR file from here: [http://bit.ly/android-bitmapcache-jar](http://bit.ly/android-bitmapcache-jar). Just remember that you must include all of the required libraries below too.
36 |
37 | If you are a Maven user you can also add this library as a dependency since it
38 | it distributed to the central repositories. Simply add the following to your
39 | `pom.xml`:
40 |
41 | ```xml
42 |
43 | com.github.chrisbanes.bitmapcache
44 | library
45 | (check pom.xml for latest version)
46 |
47 | ```
48 |
49 | ### Requirements
50 |
51 | * [DiskLruCache](https://github.com/JakeWharton/DiskLruCache). Latest available.
52 | * [Android v4 Support Library](http://developer.android.com/tools/extras/support-library.html). Latest available.
53 |
54 | ## License
55 |
56 | Copyright 2011, 2012, 2013 Chris Banes
57 |
58 | Licensed under the Apache License, Version 2.0 (the "License");
59 | you may not use this file except in compliance with the License.
60 | You may obtain a copy of the License at
61 |
62 | http://www.apache.org/licenses/LICENSE-2.0
63 |
64 | Unless required by applicable law or agreed to in writing, software
65 | distributed under the License is distributed on an "AS IS" BASIS,
66 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
67 | See the License for the specific language governing permissions and
68 | limitations under the License.
--------------------------------------------------------------------------------
/library/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/library/libs/android-support-v4.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrisbanes/Android-BitmapCache/147e5c078324c8f61765750da756c4a7d1f2aae3/library/libs/android-support-v4.jar
--------------------------------------------------------------------------------
/library/libs/disklrucache-2.0.1.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrisbanes/Android-BitmapCache/147e5c078324c8f61765750da756c4a7d1f2aae3/library/libs/disklrucache-2.0.1.jar
--------------------------------------------------------------------------------
/library/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 | com.github.chrisbanes.bitmapcache
6 | library
7 | jar
8 | Android-BitmapCache Library
9 |
10 |
11 | com.github.chrisbanes.bitmapcache
12 | parent
13 | 2.3
14 |
15 |
16 |
17 |
18 | com.google.android
19 | android
20 |
21 |
22 | com.jakewharton
23 | disklrucache
24 | jar
25 | 2.0.1
26 |
27 |
28 | com.google.android
29 | support-v4
30 | r7
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/library/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-16
15 | android.library=true
16 |
--------------------------------------------------------------------------------
/library/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Android-BitmapMemoryCache
4 |
5 |
--------------------------------------------------------------------------------
/library/src/uk/co/senab/bitmapcache/BitmapLruCache.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright (c) 2013 Chris Banes.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | ******************************************************************************/
16 |
17 | package uk.co.senab.bitmapcache;
18 |
19 | import com.jakewharton.disklrucache.DiskLruCache;
20 |
21 | import android.content.Context;
22 | import android.content.res.Resources;
23 | import android.graphics.Bitmap;
24 | import android.graphics.BitmapFactory;
25 | import android.os.AsyncTask;
26 | import android.os.Build;
27 | import android.os.Looper;
28 | import android.os.Process;
29 | import android.util.Log;
30 |
31 | import java.io.File;
32 | import java.io.FileInputStream;
33 | import java.io.FileNotFoundException;
34 | import java.io.IOException;
35 | import java.io.InputStream;
36 | import java.io.OutputStream;
37 | import java.util.HashMap;
38 | import java.util.concurrent.ScheduledFuture;
39 | import java.util.concurrent.ScheduledThreadPoolExecutor;
40 | import java.util.concurrent.TimeUnit;
41 | import java.util.concurrent.locks.ReentrantLock;
42 |
43 | /**
44 | * A cache which can be set to use multiple layers of caching for Bitmap objects in an Android app.
45 | * Instances are created via a {@link Builder} instance, which can be used to alter the settings of
46 | * the resulting cache.
47 | *
48 | *
Instances of this class should ideally be kept globally with the application, for example in
49 | * the {@link android.app.Application Application} object. You should also use the bundled {@link
50 | * CacheableImageView} wherever possible, as the memory cache has a closeStream relationship with it.
51 | *
52 | *
53 | *
Clients can call {@link #get(String)} to retrieve a cached value from the given Url. This
54 | * will check all available caches for the value. There are also the {@link
55 | * #getFromDiskCache(String, android.graphics.BitmapFactory.Options)} and {@link
56 | * #getFromMemoryCache(String)} which allow more granular access.
57 | *
58 | *
There are a number of update methods. {@link #put(String, InputStream)} and {@link
59 | * #put(String, InputStream)} are the preferred versions of the method, as they allow 1:1 caching to
60 | * disk of the original content. {@link #put(String, Bitmap)}} should only be used if you
61 | * can't get access to the original InputStream.
62 | *
63 | * @author Chris Banes
64 | */
65 | public class BitmapLruCache {
66 |
67 | /**
68 | * The recycle policy controls if the {@link android.graphics.Bitmap#recycle()} is automatically
69 | * called, when it is no longer being used. To set this, use the {@link
70 | * Builder#setRecyclePolicy(uk.co.senab.bitmapcache.BitmapLruCache.RecyclePolicy)
71 | * Builder.setRecyclePolicy()} method.
72 | */
73 | public static enum RecyclePolicy {
74 | /**
75 | * The Bitmap is never recycled automatically.
76 | */
77 | DISABLED,
78 |
79 | /**
80 | * The Bitmap is only automatically recycled if running on a device API v10 or earlier.
81 | */
82 | PRE_HONEYCOMB_ONLY,
83 |
84 | /**
85 | * The Bitmap is always recycled when no longer being used. This is the default.
86 | */
87 | ALWAYS;
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 |
98 | boolean canRecycle() {
99 | switch (this) {
100 | case DISABLED:
101 | return false;
102 | case PRE_HONEYCOMB_ONLY:
103 | return Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB;
104 | case ALWAYS:
105 | return true;
106 | }
107 |
108 | return false;
109 | }
110 | }
111 |
112 | // The number of seconds after the last edit that the Disk Cache should be
113 | // flushed
114 | static final int DISK_CACHE_FLUSH_DELAY_SECS = 5;
115 |
116 | /**
117 | * @throws IllegalStateException if the calling thread is the main/UI thread.
118 | */
119 | private static void checkNotOnMainThread() {
120 | if (Looper.myLooper() == Looper.getMainLooper()) {
121 | throw new IllegalStateException(
122 | "This method should not be called from the main/UI thread.");
123 | }
124 | }
125 |
126 | /**
127 | * The disk cache only accepts a reduced range of characters for the key values. This method
128 | * transforms the {@code url} into something accepted from {@link DiskLruCache}. Currently we
129 | * simply return a MD5 hash of the url.
130 | *
131 | * @param url - Key to be transformed
132 | * @return key which can be used for the disk cache
133 | */
134 | private static String transformUrlForDiskCacheKey(String url) {
135 | return Md5.encode(url);
136 | }
137 |
138 | private File mTempDir;
139 |
140 | private Resources mResources;
141 |
142 | /**
143 | * Memory Cache Variables
144 | */
145 | private BitmapMemoryLruCache mMemoryCache;
146 |
147 | private RecyclePolicy mRecyclePolicy;
148 |
149 | /**
150 | * Disk Cache Variables
151 | */
152 | private DiskLruCache mDiskCache;
153 |
154 | // Variables which are only used when the Disk Cache is enabled
155 | private HashMap mDiskCacheEditLocks;
156 |
157 | private ScheduledThreadPoolExecutor mDiskCacheFlusherExecutor;
158 |
159 | private DiskCacheFlushRunnable mDiskCacheFlusherRunnable;
160 |
161 | // Transient
162 | private ScheduledFuture> mDiskCacheFuture;
163 |
164 | BitmapLruCache(Context context) {
165 | if (null != context) {
166 | // Make sure we have the application context
167 | context = context.getApplicationContext();
168 |
169 | mTempDir = context.getCacheDir();
170 | mResources = context.getResources();
171 | }
172 | }
173 |
174 | /**
175 | * Returns whether any of the enabled caches contain the specified URL. If you have the
176 | * disk cache enabled, you should not call this method from main/UI thread.
177 | *
178 | * @param url the URL to search for.
179 | * @return {@code true} if any of the caches contain the specified URL, {@code false}
180 | * otherwise.
181 | */
182 | public boolean contains(String url) {
183 | return containsInMemoryCache(url) || containsInDiskCache(url);
184 | }
185 |
186 | /**
187 | * Returns whether the Disk Cache contains the specified URL. You should not call this method
188 | * from main/UI thread.
189 | *
190 | * @param url the URL to search for.
191 | * @return {@code true} if the Disk Cache is enabled and contains the specified URL, {@code
192 | * false} otherwise.
193 | */
194 | public boolean containsInDiskCache(String url) {
195 | if (null != mDiskCache) {
196 | checkNotOnMainThread();
197 |
198 | try {
199 | return null != mDiskCache.get(transformUrlForDiskCacheKey(url));
200 | } catch (IOException e) {
201 | e.printStackTrace();
202 | }
203 | }
204 |
205 | return false;
206 | }
207 |
208 | /**
209 | * Returns whether the Memory Cache contains the specified URL. This method is safe to be called
210 | * from the main thread.
211 | *
212 | * @param url the URL to search for.
213 | * @return {@code true} if the Memory Cache is enabled and contains the specified URL, {@code
214 | * false} otherwise.
215 | */
216 | public boolean containsInMemoryCache(String url) {
217 | return null != mMemoryCache && null != mMemoryCache.get(url);
218 | }
219 |
220 | /**
221 | * Returns the value for {@code url}. This will check all caches currently enabled. If you
222 | * have the disk cache enabled, you should not call this method from main/UI thread.
223 | *
224 | * @param url - String representing the URL of the image
225 | */
226 | public CacheableBitmapDrawable get(String url) {
227 | return get(url, null);
228 | }
229 |
230 | /**
231 | * Returns the value for {@code url}. This will check all caches currently enabled. If you
232 | * have the disk cache enabled, you should not call this method from main/UI thread.
233 | *
234 | * @param url - String representing the URL of the image
235 | * @param decodeOpts - Options used for decoding the contents from the disk cache only.
236 | */
237 | public CacheableBitmapDrawable get(String url, BitmapFactory.Options decodeOpts) {
238 | CacheableBitmapDrawable result;
239 |
240 | // First try Memory Cache
241 | result = getFromMemoryCache(url);
242 |
243 | if (null == result) {
244 | // Memory Cache failed, so try Disk Cache
245 | result = getFromDiskCache(url, decodeOpts);
246 | }
247 |
248 | return result;
249 | }
250 |
251 | /**
252 | * Returns the value for {@code url} in the disk cache only. You should not call this method
253 | * from main/UI thread. If enabled, the result of this method will be cached in the memory
254 | * cache. Unless you have a specific requirement to only query the disk cache, you should
255 | * call {@link #get(String)} instead.
256 | *
257 | * @param url - String representing the URL of the image
258 | * @param decodeOpts - Options used for decoding the contents from the disk cache.
259 | * @return Value for {@code url} from disk cache, or {@code null} if the disk cache is not
260 | * enabled.
261 | */
262 | public CacheableBitmapDrawable getFromDiskCache(final String url,
263 | final BitmapFactory.Options decodeOpts) {
264 | CacheableBitmapDrawable result = null;
265 |
266 | if (null != mDiskCache) {
267 | checkNotOnMainThread();
268 |
269 | try {
270 | final String key = transformUrlForDiskCacheKey(url);
271 | // Try and decode bitmap
272 | result = decodeBitmap(new SnapshotInputStreamProvider(key), url, decodeOpts);
273 |
274 | if (null != result) {
275 | if (null != mMemoryCache) {
276 | mMemoryCache.put(result);
277 | }
278 | } else {
279 | // If we get here, the file in the cache can't be
280 | // decoded. Remove it and schedule a flush.
281 | mDiskCache.remove(key);
282 | scheduleDiskCacheFlush();
283 | }
284 | } catch (IOException e) {
285 | e.printStackTrace();
286 | }
287 | }
288 |
289 | return result;
290 | }
291 |
292 | /**
293 | * Returns the value for {@code url} in the memory cache only. This method is safe to be called
294 | * from the main thread. You should check the result of this method before starting a
295 | * threaded call.
296 | *
297 | * @param url - String representing the URL of the image
298 | * @return Value for {@code url} from memory cache, or {@code null} if the disk cache is not
299 | * enabled.
300 | */
301 | public CacheableBitmapDrawable getFromMemoryCache(final String url) {
302 | CacheableBitmapDrawable result = null;
303 |
304 | if (null != mMemoryCache) {
305 | synchronized (mMemoryCache) {
306 | result = mMemoryCache.get(url);
307 |
308 | // If we get a value, but it has a invalid bitmap, remove it
309 | if (null != result && !result.isBitmapValid()) {
310 | mMemoryCache.remove(url);
311 | result = null;
312 | }
313 | }
314 | }
315 |
316 | return result;
317 | }
318 |
319 | /**
320 | * @return true if the Disk Cache is enabled.
321 | */
322 | public boolean isDiskCacheEnabled() {
323 | return null != mDiskCache;
324 | }
325 |
326 | /**
327 | * @return true if the Memory Cache is enabled.
328 | */
329 | public boolean isMemoryCacheEnabled() {
330 | return null != mMemoryCache;
331 | }
332 |
333 | /**
334 | * Caches {@code bitmap} for {@code url} into all enabled caches. If the disk cache is enabled,
335 | * the bitmap will be compressed losslessly. If you have the disk cache enabled, you should
336 | * not call this method from main/UI thread.
337 | *
338 | * @param url - String representing the URL of the image.
339 | * @param bitmap - Bitmap which has been decoded from {@code url}.
340 | * @return CacheableBitmapDrawable which can be used to display the bitmap.
341 | */
342 | public CacheableBitmapDrawable put(final String url, final Bitmap bitmap) {
343 | return put(url, bitmap, Bitmap.CompressFormat.PNG, 100);
344 | }
345 |
346 | /**
347 | * Caches {@code bitmap} for {@code url} into all enabled caches. If the disk cache is enabled,
348 | * the bitmap will be compressed with the settings you provide.
349 | * If you have the disk cache enabled, you should not call this method from main/UI thread.
350 | *
351 | * @param url - String representing the URL of the image.
352 | * @param bitmap - Bitmap which has been decoded from {@code url}.
353 | * @param compressFormat - Compression Format to use
354 | * @param compressQuality - Level of compression to use
355 | * @return CacheableBitmapDrawable which can be used to display the bitmap.
356 | *
357 | * @see Bitmap#compress(Bitmap.CompressFormat, int, OutputStream)
358 | */
359 | public CacheableBitmapDrawable put(final String url, final Bitmap bitmap,
360 | Bitmap.CompressFormat compressFormat, int compressQuality) {
361 |
362 | CacheableBitmapDrawable d = new CacheableBitmapDrawable(url, mResources, bitmap,
363 | mRecyclePolicy, CacheableBitmapDrawable.SOURCE_UNKNOWN);
364 |
365 | if (null != mMemoryCache) {
366 | mMemoryCache.put(d);
367 | }
368 |
369 | if (null != mDiskCache) {
370 | checkNotOnMainThread();
371 |
372 | final String key = transformUrlForDiskCacheKey(url);
373 | final ReentrantLock lock = getLockForDiskCacheEdit(key);
374 | lock.lock();
375 |
376 | OutputStream os = null;
377 |
378 | try {
379 | DiskLruCache.Editor editor = mDiskCache.edit(key);
380 | os = editor.newOutputStream(0);
381 | bitmap.compress(compressFormat, compressQuality, os);
382 | os.flush();
383 | editor.commit();
384 | } catch (IOException e) {
385 | Log.e(Constants.LOG_TAG, "Error while writing to disk cache", e);
386 | } finally {
387 | IoUtils.closeStream(os);
388 | lock.unlock();
389 | scheduleDiskCacheFlush();
390 | }
391 | }
392 |
393 | return d;
394 | }
395 |
396 | /**
397 | * Caches resulting bitmap from {@code inputStream} for {@code url} into all enabled caches.
398 | * This version of the method should be preferred as it allows the original image contents to be
399 | * cached, rather than a re-compressed version. The contents of the InputStream will be
400 | * copied to a temporary file, then the file will be decoded into a Bitmap. Providing the decode
401 | * worked:
If the memory cache is enabled, the decoded Bitmap will be cached to
402 | * memory.
If the disk cache is enabled, the contents of the original stream will be
403 | * cached to disk.
You should not call this method from the main/UI thread.
404 | *
405 | * @param url - String representing the URL of the image
406 | * @param inputStream - InputStream opened from {@code url}
407 | * @return CacheableBitmapDrawable which can be used to display the bitmap.
408 | */
409 | public CacheableBitmapDrawable put(final String url, final InputStream inputStream) {
410 | return put(url, inputStream, null);
411 | }
412 |
413 | /**
414 | * Caches resulting bitmap from {@code inputStream} for {@code url} into all enabled caches.
415 | * This version of the method should be preferred as it allows the original image contents to be
416 | * cached, rather than a re-compressed version. The contents of the InputStream will be
417 | * copied to a temporary file, then the file will be decoded into a Bitmap, using the optional
418 | * decodeOpts. Providing the decode worked:
If the memory cache is
419 | * enabled, the decoded Bitmap will be cached to memory.
If the disk cache is enabled,
420 | * the contents of the original stream will be cached to disk.
You should not
421 | * call this method from the main/UI thread.
422 | *
423 | * @param url - String representing the URL of the image
424 | * @param inputStream - InputStream opened from {@code url}
425 | * @param decodeOpts - Options used for decoding. This does not affect what is cached in the
426 | * disk cache (if enabled).
427 | * @return CacheableBitmapDrawable which can be used to display the bitmap.
428 | */
429 | public CacheableBitmapDrawable put(final String url, final InputStream inputStream,
430 | final BitmapFactory.Options decodeOpts) {
431 | checkNotOnMainThread();
432 |
433 | // First we need to save the stream contents to a temporary file, so it
434 | // can be read multiple times
435 | File tmpFile = null;
436 | try {
437 | tmpFile = File.createTempFile("bitmapcache_", null, mTempDir);
438 |
439 | // Pipe InputStream to file
440 | IoUtils.copy(inputStream, tmpFile);
441 | } catch (IOException e) {
442 | Log.e(Constants.LOG_TAG, "Error writing to saving stream to temp file: " + url, e);
443 | }
444 |
445 | CacheableBitmapDrawable d = null;
446 |
447 | if (null != tmpFile) {
448 | // Try and decode File
449 | d = decodeBitmap(new FileInputStreamProvider(tmpFile), url, decodeOpts);
450 |
451 | if (d != null) {
452 | if (null != mMemoryCache) {
453 | d.setCached(true);
454 | mMemoryCache.put(d.getUrl(), d);
455 | }
456 |
457 | if (null != mDiskCache) {
458 | final String key = transformUrlForDiskCacheKey(url);
459 | final ReentrantLock lock = getLockForDiskCacheEdit(url);
460 | lock.lock();
461 |
462 | try {
463 | DiskLruCache.Editor editor = mDiskCache.edit(key);
464 | IoUtils.copy(tmpFile, editor.newOutputStream(0));
465 | editor.commit();
466 | } catch (IOException e) {
467 | Log.e(Constants.LOG_TAG, "Error writing to disk cache. URL: " + url, e);
468 | } finally {
469 | lock.unlock();
470 | scheduleDiskCacheFlush();
471 | }
472 | }
473 | }
474 |
475 | // Finally, delete the temporary file
476 | tmpFile.delete();
477 | }
478 |
479 | return d;
480 | }
481 |
482 | /**
483 | * Removes the entry for {@code url} from all enabled caches, if it exists. If you have the
484 | * disk cache enabled, you should not call this method from main/UI thread.
485 | */
486 | public void remove(String url) {
487 | if (null != mMemoryCache) {
488 | mMemoryCache.remove(url);
489 | }
490 |
491 | if (null != mDiskCache) {
492 | checkNotOnMainThread();
493 |
494 | try {
495 | mDiskCache.remove(transformUrlForDiskCacheKey(url));
496 | scheduleDiskCacheFlush();
497 | } catch (IOException e) {
498 | e.printStackTrace();
499 | }
500 | }
501 | }
502 |
503 | /**
504 | * This method iterates through the memory cache (if enabled) and removes any entries which are
505 | * not currently being displayed. A good place to call this would be from {@link
506 | * android.app.Application#onLowMemory() Application.onLowMemory()}.
507 | */
508 | public void trimMemory() {
509 | if (null != mMemoryCache) {
510 | mMemoryCache.trimMemory();
511 | }
512 | }
513 |
514 | synchronized void setDiskCache(DiskLruCache diskCache) {
515 | mDiskCache = diskCache;
516 |
517 | if (null != diskCache) {
518 | mDiskCacheEditLocks = new HashMap();
519 | mDiskCacheFlusherExecutor = new ScheduledThreadPoolExecutor(1);
520 | mDiskCacheFlusherRunnable = new DiskCacheFlushRunnable(diskCache);
521 | }
522 | }
523 |
524 | void setMemoryCache(BitmapMemoryLruCache memoryCache) {
525 | mMemoryCache = memoryCache;
526 | mRecyclePolicy = memoryCache.getRecyclePolicy();
527 | }
528 |
529 | private ReentrantLock getLockForDiskCacheEdit(String url) {
530 | synchronized (mDiskCacheEditLocks) {
531 | ReentrantLock lock = mDiskCacheEditLocks.get(url);
532 | if (null == lock) {
533 | lock = new ReentrantLock();
534 | mDiskCacheEditLocks.put(url, lock);
535 | }
536 | return lock;
537 | }
538 | }
539 |
540 | private void scheduleDiskCacheFlush() {
541 | // If we already have a flush scheduled, cancel it
542 | if (null != mDiskCacheFuture) {
543 | mDiskCacheFuture.cancel(false);
544 | }
545 |
546 | // Schedule a flush
547 | mDiskCacheFuture = mDiskCacheFlusherExecutor
548 | .schedule(mDiskCacheFlusherRunnable, DISK_CACHE_FLUSH_DELAY_SECS,
549 | TimeUnit.SECONDS);
550 | }
551 |
552 | private CacheableBitmapDrawable decodeBitmap(InputStreamProvider ip, String url,
553 | BitmapFactory.Options opts) {
554 |
555 | Bitmap bm = null;
556 | InputStream is = null;
557 | int source = CacheableBitmapDrawable.SOURCE_NEW;
558 |
559 | try {
560 | if (mRecyclePolicy.canInBitmap()) {
561 | // Create an options instance if we haven't been provided with one
562 | if (opts == null) {
563 | opts = new BitmapFactory.Options();
564 | }
565 |
566 | if (opts.inSampleSize <= 1) {
567 | opts.inSampleSize = 1;
568 |
569 | if (addInBitmapOptions(ip, opts)) {
570 | source = CacheableBitmapDrawable.SOURCE_INBITMAP;
571 | }
572 | }
573 | }
574 |
575 | // Get InputStream for actual decode
576 | is = ip.getInputStream();
577 | // Decode stream
578 | bm = BitmapFactory.decodeStream(is, null, opts);
579 | } catch (Exception e) {
580 | Log.e(Constants.LOG_TAG, "Unable to decode stream", e);
581 | } finally {
582 | IoUtils.closeStream(is);
583 | }
584 |
585 | if (bm != null) {
586 | return new CacheableBitmapDrawable(url, mResources, bm, mRecyclePolicy, source);
587 | }
588 | return null;
589 | }
590 |
591 | private boolean addInBitmapOptions(InputStreamProvider ip, BitmapFactory.Options opts) {
592 | // Create InputStream for decoding the bounds
593 | final InputStream is = ip.getInputStream();
594 | // Decode the bounds so we know what size Bitmap to look for
595 | opts.inJustDecodeBounds = true;
596 | BitmapFactory.decodeStream(is, null, opts);
597 | IoUtils.closeStream(is);
598 |
599 | // Turn off just decoding bounds
600 | opts.inJustDecodeBounds = false;
601 | // Make sure the decoded file is mutable
602 | opts.inMutable = true;
603 |
604 | // Try and find Bitmap to use for inBitmap
605 | Bitmap reusableBm = mMemoryCache.getBitmapFromRemoved(opts.outWidth, opts.outHeight);
606 | if (reusableBm != null) {
607 | if (Constants.DEBUG) {
608 | Log.i(Constants.LOG_TAG, "Using inBitmap");
609 | }
610 | SDK11.addInBitmapOption(opts, reusableBm);
611 | return true;
612 | }
613 |
614 | return false;
615 | }
616 |
617 | /**
618 | * Builder class for {link {@link BitmapLruCache}. An example call:
619 | *
620 | *