1 /*
2  * Copyright (C) 2013 The Android Open Source Project
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 com.example.android.displayingbitmaps.util;
18 
19 import android.content.res.Resources;
20 import android.graphics.Bitmap;
21 import android.graphics.drawable.BitmapDrawable;
22 
23 import com.example.android.common.logger.Log;
24 import com.example.android.displayingbitmaps.BuildConfig;
25 
26 /**
27  * A BitmapDrawable that keeps track of whether it is being displayed or cached.
28  * When the drawable is no longer being displayed or cached,
29  * {@link android.graphics.Bitmap#recycle() recycle()} will be called on this drawable's bitmap.
30  */
31 public class RecyclingBitmapDrawable extends BitmapDrawable {
32 
33     static final String TAG = "CountingBitmapDrawable";
34 
35     private int mCacheRefCount = 0;
36     private int mDisplayRefCount = 0;
37 
38     private boolean mHasBeenDisplayed;
39 
RecyclingBitmapDrawable(Resources res, Bitmap bitmap)40     public RecyclingBitmapDrawable(Resources res, Bitmap bitmap) {
41         super(res, bitmap);
42     }
43 
44     /**
45      * Notify the drawable that the displayed state has changed. Internally a
46      * count is kept so that the drawable knows when it is no longer being
47      * displayed.
48      *
49      * @param isDisplayed - Whether the drawable is being displayed or not
50      */
setIsDisplayed(boolean isDisplayed)51     public void setIsDisplayed(boolean isDisplayed) {
52         //BEGIN_INCLUDE(set_is_displayed)
53         synchronized (this) {
54             if (isDisplayed) {
55                 mDisplayRefCount++;
56                 mHasBeenDisplayed = true;
57             } else {
58                 mDisplayRefCount--;
59             }
60         }
61 
62         // Check to see if recycle() can be called
63         checkState();
64         //END_INCLUDE(set_is_displayed)
65     }
66 
67     /**
68      * Notify the drawable that the cache state has changed. Internally a count
69      * is kept so that the drawable knows when it is no longer being cached.
70      *
71      * @param isCached - Whether the drawable is being cached or not
72      */
setIsCached(boolean isCached)73     public void setIsCached(boolean isCached) {
74         //BEGIN_INCLUDE(set_is_cached)
75         synchronized (this) {
76             if (isCached) {
77                 mCacheRefCount++;
78             } else {
79                 mCacheRefCount--;
80             }
81         }
82 
83         // Check to see if recycle() can be called
84         checkState();
85         //END_INCLUDE(set_is_cached)
86     }
87 
checkState()88     private synchronized void checkState() {
89         //BEGIN_INCLUDE(check_state)
90         // If the drawable cache and display ref counts = 0, and this drawable
91         // has been displayed, then recycle
92         if (mCacheRefCount <= 0 && mDisplayRefCount <= 0 && mHasBeenDisplayed
93                 && hasValidBitmap()) {
94             if (BuildConfig.DEBUG) {
95                 Log.d(TAG, "No longer being used or cached so recycling. "
96                         + toString());
97             }
98 
99             getBitmap().recycle();
100         }
101         //END_INCLUDE(check_state)
102     }
103 
hasValidBitmap()104     private synchronized boolean hasValidBitmap() {
105         Bitmap bitmap = getBitmap();
106         return bitmap != null && !bitmap.isRecycled();
107     }
108 
109 }
110