Merge "Update bitmapfun sample to call recycle()" into jb-mr1-dev
This commit is contained in:
@@ -25,7 +25,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center" />
|
android:layout_gravity="center" />
|
||||||
|
|
||||||
<ImageView
|
<com.example.android.bitmapfun.ui.RecyclingImageView
|
||||||
android:id="@+id/imageView"
|
android:id="@+id/imageView"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="fill_parent"
|
android:layout_height="fill_parent"
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ package com.example.android.bitmapfun.ui;
|
|||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
import android.util.Log;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.View.OnClickListener;
|
import android.view.View.OnClickListener;
|
||||||
|
|||||||
@@ -270,7 +270,7 @@ public class ImageGridFragment extends Fragment implements AdapterView.OnItemCli
|
|||||||
// Now handle the main ImageView thumbnails
|
// Now handle the main ImageView thumbnails
|
||||||
ImageView imageView;
|
ImageView imageView;
|
||||||
if (convertView == null) { // if it's not recycled, instantiate and initialize
|
if (convertView == null) { // if it's not recycled, instantiate and initialize
|
||||||
imageView = new ImageView(mContext);
|
imageView = new RecyclingImageView(mContext);
|
||||||
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
|
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
|
||||||
imageView.setLayoutParams(mImageViewLayoutParams);
|
imageView.setLayoutParams(mImageViewLayoutParams);
|
||||||
} else { // Otherwise re-use the converted view
|
} else { // Otherwise re-use the converted view
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.example.android.bitmapfun.ui;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.graphics.drawable.LayerDrawable;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
|
||||||
|
import com.example.android.bitmapfun.util.RecyclingBitmapDrawable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sub-class of ImageView which automatically notifies the drawable when it is
|
||||||
|
* being displayed.
|
||||||
|
*/
|
||||||
|
public class RecyclingImageView extends ImageView {
|
||||||
|
|
||||||
|
public RecyclingImageView(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RecyclingImageView(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see android.widget.ImageView#onDetachedFromWindow()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void onDetachedFromWindow() {
|
||||||
|
// This has been detached from Window, so clear the drawable
|
||||||
|
setImageDrawable(null);
|
||||||
|
|
||||||
|
super.onDetachedFromWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see android.widget.ImageView#setImageDrawable(android.graphics.drawable.Drawable)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setImageDrawable(Drawable drawable) {
|
||||||
|
// Keep hold of previous Drawable
|
||||||
|
final Drawable previousDrawable = getDrawable();
|
||||||
|
|
||||||
|
// Call super to set new Drawable
|
||||||
|
super.setImageDrawable(drawable);
|
||||||
|
|
||||||
|
// Notify new Drawable that it is being displayed
|
||||||
|
notifyDrawable(drawable, true);
|
||||||
|
|
||||||
|
// Notify old Drawable so it is no longer being displayed
|
||||||
|
notifyDrawable(previousDrawable, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the drawable that it's displayed state has changed.
|
||||||
|
*
|
||||||
|
* @param drawable
|
||||||
|
* @param isDisplayed
|
||||||
|
*/
|
||||||
|
private static void notifyDrawable(Drawable drawable, final boolean isDisplayed) {
|
||||||
|
if (drawable instanceof RecyclingBitmapDrawable) {
|
||||||
|
// The drawable is a CountingBitmapDrawable, so notify it
|
||||||
|
((RecyclingBitmapDrawable) drawable).setIsDisplayed(isDisplayed);
|
||||||
|
} else if (drawable instanceof LayerDrawable) {
|
||||||
|
// The drawable is a LayerDrawable, so recurse on each layer
|
||||||
|
LayerDrawable layerDrawable = (LayerDrawable) drawable;
|
||||||
|
for (int i = 0, z = layerDrawable.getNumberOfLayers(); i < z; i++) {
|
||||||
|
notifyDrawable(layerDrawable.getDrawable(i), isDisplayed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -17,10 +17,10 @@
|
|||||||
package com.example.android.bitmapfun.util;
|
package com.example.android.bitmapfun.util;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.app.ActivityManager;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Bitmap.CompressFormat;
|
import android.graphics.Bitmap.CompressFormat;
|
||||||
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
@@ -63,7 +63,7 @@ public class ImageCache {
|
|||||||
private static final boolean DEFAULT_INIT_DISK_CACHE_ON_CREATE = false;
|
private static final boolean DEFAULT_INIT_DISK_CACHE_ON_CREATE = false;
|
||||||
|
|
||||||
private DiskLruCache mDiskLruCache;
|
private DiskLruCache mDiskLruCache;
|
||||||
private LruCache<String, Bitmap> mMemoryCache;
|
private LruCache<String, BitmapDrawable> mMemoryCache;
|
||||||
private ImageCacheParams mCacheParams;
|
private ImageCacheParams mCacheParams;
|
||||||
private final Object mDiskCacheLock = new Object();
|
private final Object mDiskCacheLock = new Object();
|
||||||
private boolean mDiskCacheStarting = true;
|
private boolean mDiskCacheStarting = true;
|
||||||
@@ -126,14 +126,28 @@ public class ImageCache {
|
|||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
Log.d(TAG, "Memory cache created (size = " + mCacheParams.memCacheSize + ")");
|
Log.d(TAG, "Memory cache created (size = " + mCacheParams.memCacheSize + ")");
|
||||||
}
|
}
|
||||||
mMemoryCache = new LruCache<String, Bitmap>(mCacheParams.memCacheSize) {
|
mMemoryCache = new LruCache<String, BitmapDrawable>(mCacheParams.memCacheSize) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify the removed entry that is no longer being cached
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void entryRemoved(boolean evicted, String key,
|
||||||
|
BitmapDrawable oldValue, BitmapDrawable newValue) {
|
||||||
|
if (RecyclingBitmapDrawable.class.isInstance(oldValue)) {
|
||||||
|
// The removed entry is a recycling drawable, so notify it
|
||||||
|
// that it has been removed from the memory cache
|
||||||
|
((RecyclingBitmapDrawable) oldValue).setIsCached(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Measure item size in kilobytes rather than units which is more practical
|
* Measure item size in kilobytes rather than units which is more practical
|
||||||
* for a bitmap cache
|
* for a bitmap cache
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected int sizeOf(String key, Bitmap bitmap) {
|
protected int sizeOf(String key, BitmapDrawable value) {
|
||||||
final int bitmapSize = getBitmapSize(bitmap) / 1024;
|
final int bitmapSize = getBitmapSize(value) / 1024;
|
||||||
return bitmapSize == 0 ? 1 : bitmapSize;
|
return bitmapSize == 0 ? 1 : bitmapSize;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -184,16 +198,21 @@ public class ImageCache {
|
|||||||
/**
|
/**
|
||||||
* Adds a bitmap to both memory and disk cache.
|
* Adds a bitmap to both memory and disk cache.
|
||||||
* @param data Unique identifier for the bitmap to store
|
* @param data Unique identifier for the bitmap to store
|
||||||
* @param bitmap The bitmap to store
|
* @param value The bitmap drawable to store
|
||||||
*/
|
*/
|
||||||
public void addBitmapToCache(String data, Bitmap bitmap) {
|
public void addBitmapToCache(String data, BitmapDrawable value) {
|
||||||
if (data == null || bitmap == null) {
|
if (data == null || value == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add to memory cache
|
// Add to memory cache
|
||||||
if (mMemoryCache != null && mMemoryCache.get(data) == null) {
|
if (mMemoryCache != null) {
|
||||||
mMemoryCache.put(data, bitmap);
|
if (RecyclingBitmapDrawable.class.isInstance(value)) {
|
||||||
|
// The removed entry is a recycling drawable, so notify it
|
||||||
|
// that it has been added into the memory cache
|
||||||
|
((RecyclingBitmapDrawable) value).setIsCached(true);
|
||||||
|
}
|
||||||
|
mMemoryCache.put(data, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized (mDiskCacheLock) {
|
synchronized (mDiskCacheLock) {
|
||||||
@@ -207,7 +226,7 @@ public class ImageCache {
|
|||||||
final DiskLruCache.Editor editor = mDiskLruCache.edit(key);
|
final DiskLruCache.Editor editor = mDiskLruCache.edit(key);
|
||||||
if (editor != null) {
|
if (editor != null) {
|
||||||
out = editor.newOutputStream(DISK_CACHE_INDEX);
|
out = editor.newOutputStream(DISK_CACHE_INDEX);
|
||||||
bitmap.compress(
|
value.getBitmap().compress(
|
||||||
mCacheParams.compressFormat, mCacheParams.compressQuality, out);
|
mCacheParams.compressFormat, mCacheParams.compressQuality, out);
|
||||||
editor.commit();
|
editor.commit();
|
||||||
out.close();
|
out.close();
|
||||||
@@ -234,19 +253,20 @@ public class ImageCache {
|
|||||||
* Get from memory cache.
|
* Get from memory cache.
|
||||||
*
|
*
|
||||||
* @param data Unique identifier for which item to get
|
* @param data Unique identifier for which item to get
|
||||||
* @return The bitmap if found in cache, null otherwise
|
* @return The bitmap drawable if found in cache, null otherwise
|
||||||
*/
|
*/
|
||||||
public Bitmap getBitmapFromMemCache(String data) {
|
public BitmapDrawable getBitmapFromMemCache(String data) {
|
||||||
|
BitmapDrawable memValue = null;
|
||||||
|
|
||||||
if (mMemoryCache != null) {
|
if (mMemoryCache != null) {
|
||||||
final Bitmap memBitmap = mMemoryCache.get(data);
|
memValue = mMemoryCache.get(data);
|
||||||
if (memBitmap != null) {
|
}
|
||||||
if (BuildConfig.DEBUG) {
|
|
||||||
|
if (BuildConfig.DEBUG && memValue != null) {
|
||||||
Log.d(TAG, "Memory cache hit");
|
Log.d(TAG, "Memory cache hit");
|
||||||
}
|
}
|
||||||
return memBitmap;
|
|
||||||
}
|
return memValue;
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -453,12 +473,14 @@ public class ImageCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the size in bytes of a bitmap.
|
* Get the size in bytes of a bitmap in a BitmapDrawable.
|
||||||
* @param bitmap
|
* @param value
|
||||||
* @return size in bytes
|
* @return size in bytes
|
||||||
*/
|
*/
|
||||||
@TargetApi(12)
|
@TargetApi(12)
|
||||||
public static int getBitmapSize(Bitmap bitmap) {
|
public static int getBitmapSize(BitmapDrawable value) {
|
||||||
|
Bitmap bitmap = value.getBitmap();
|
||||||
|
|
||||||
if (Utils.hasHoneycombMR1()) {
|
if (Utils.hasHoneycombMR1()) {
|
||||||
return bitmap.getByteCount();
|
return bitmap.getByteCount();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,15 +76,15 @@ public abstract class ImageWorker {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Bitmap bitmap = null;
|
BitmapDrawable value = null;
|
||||||
|
|
||||||
if (mImageCache != null) {
|
if (mImageCache != null) {
|
||||||
bitmap = mImageCache.getBitmapFromMemCache(String.valueOf(data));
|
value = mImageCache.getBitmapFromMemCache(String.valueOf(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bitmap != null) {
|
if (value != null) {
|
||||||
// Bitmap found in memory cache
|
// Bitmap found in memory cache
|
||||||
imageView.setImageBitmap(bitmap);
|
imageView.setImageDrawable(value);
|
||||||
} else if (cancelPotentialWork(data, imageView)) {
|
} else if (cancelPotentialWork(data, imageView)) {
|
||||||
final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
|
final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
|
||||||
final AsyncDrawable asyncDrawable =
|
final AsyncDrawable asyncDrawable =
|
||||||
@@ -222,7 +222,7 @@ public abstract class ImageWorker {
|
|||||||
/**
|
/**
|
||||||
* The actual AsyncTask that will asynchronously process the image.
|
* The actual AsyncTask that will asynchronously process the image.
|
||||||
*/
|
*/
|
||||||
private class BitmapWorkerTask extends AsyncTask<Object, Void, Bitmap> {
|
private class BitmapWorkerTask extends AsyncTask<Object, Void, BitmapDrawable> {
|
||||||
private Object data;
|
private Object data;
|
||||||
private final WeakReference<ImageView> imageViewReference;
|
private final WeakReference<ImageView> imageViewReference;
|
||||||
|
|
||||||
@@ -234,7 +234,7 @@ public abstract class ImageWorker {
|
|||||||
* Background processing.
|
* Background processing.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected Bitmap doInBackground(Object... params) {
|
protected BitmapDrawable doInBackground(Object... params) {
|
||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
Log.d(TAG, "doInBackground - starting work");
|
Log.d(TAG, "doInBackground - starting work");
|
||||||
}
|
}
|
||||||
@@ -242,6 +242,7 @@ public abstract class ImageWorker {
|
|||||||
data = params[0];
|
data = params[0];
|
||||||
final String dataString = String.valueOf(data);
|
final String dataString = String.valueOf(data);
|
||||||
Bitmap bitmap = null;
|
Bitmap bitmap = null;
|
||||||
|
BitmapDrawable drawable = null;
|
||||||
|
|
||||||
// Wait here if work is paused and the task is not cancelled
|
// Wait here if work is paused and the task is not cancelled
|
||||||
synchronized (mPauseWorkLock) {
|
synchronized (mPauseWorkLock) {
|
||||||
@@ -274,39 +275,50 @@ public abstract class ImageWorker {
|
|||||||
// bitmap to the cache for future use. Note we don't check if the task was cancelled
|
// bitmap to the cache for future use. Note we don't check if the task was cancelled
|
||||||
// here, if it was, and the thread is still running, we may as well add the processed
|
// here, if it was, and the thread is still running, we may as well add the processed
|
||||||
// bitmap to our cache as it might be used again in the future
|
// bitmap to our cache as it might be used again in the future
|
||||||
if (bitmap != null && mImageCache != null) {
|
if (bitmap != null) {
|
||||||
mImageCache.addBitmapToCache(dataString, bitmap);
|
if (Utils.hasHoneycomb()) {
|
||||||
|
// Running on Honeycomb or newer, so wrap in a standard BitmapDrawable
|
||||||
|
drawable = new BitmapDrawable(mResources, bitmap);
|
||||||
|
} else {
|
||||||
|
// Running on Gingerbread or older, so wrap in a RecyclingBitmapDrawable
|
||||||
|
// which will recycle automagically
|
||||||
|
drawable = new RecyclingBitmapDrawable(mResources, bitmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mImageCache != null) {
|
||||||
|
mImageCache.addBitmapToCache(dataString, drawable);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
Log.d(TAG, "doInBackground - finished work");
|
Log.d(TAG, "doInBackground - finished work");
|
||||||
}
|
}
|
||||||
|
|
||||||
return bitmap;
|
return drawable;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Once the image is processed, associates it to the imageView
|
* Once the image is processed, associates it to the imageView
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(Bitmap bitmap) {
|
protected void onPostExecute(BitmapDrawable value) {
|
||||||
// if cancel was called on this task or the "exit early" flag is set then we're done
|
// if cancel was called on this task or the "exit early" flag is set then we're done
|
||||||
if (isCancelled() || mExitTasksEarly) {
|
if (isCancelled() || mExitTasksEarly) {
|
||||||
bitmap = null;
|
value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final ImageView imageView = getAttachedImageView();
|
final ImageView imageView = getAttachedImageView();
|
||||||
if (bitmap != null && imageView != null) {
|
if (value != null && imageView != null) {
|
||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
Log.d(TAG, "onPostExecute - setting bitmap");
|
Log.d(TAG, "onPostExecute - setting bitmap");
|
||||||
}
|
}
|
||||||
setImageBitmap(imageView, bitmap);
|
setImageDrawable(imageView, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCancelled(Bitmap bitmap) {
|
protected void onCancelled(BitmapDrawable value) {
|
||||||
super.onCancelled(bitmap);
|
super.onCancelled(value);
|
||||||
synchronized (mPauseWorkLock) {
|
synchronized (mPauseWorkLock) {
|
||||||
mPauseWorkLock.notifyAll();
|
mPauseWorkLock.notifyAll();
|
||||||
}
|
}
|
||||||
@@ -349,18 +361,19 @@ public abstract class ImageWorker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the processing is complete and the final bitmap should be set on the ImageView.
|
* Called when the processing is complete and the final drawable should be
|
||||||
|
* set on the ImageView.
|
||||||
*
|
*
|
||||||
* @param imageView
|
* @param imageView
|
||||||
* @param bitmap
|
* @param drawable
|
||||||
*/
|
*/
|
||||||
private void setImageBitmap(ImageView imageView, Bitmap bitmap) {
|
private void setImageDrawable(ImageView imageView, Drawable drawable) {
|
||||||
if (mFadeInBitmap) {
|
if (mFadeInBitmap) {
|
||||||
// Transition drawable with a transparent drwabale and the final bitmap
|
// Transition drawable with a transparent drawable and the final drawable
|
||||||
final TransitionDrawable td =
|
final TransitionDrawable td =
|
||||||
new TransitionDrawable(new Drawable[] {
|
new TransitionDrawable(new Drawable[] {
|
||||||
new ColorDrawable(android.R.color.transparent),
|
new ColorDrawable(android.R.color.transparent),
|
||||||
new BitmapDrawable(mResources, bitmap)
|
drawable
|
||||||
});
|
});
|
||||||
// Set background to loading bitmap
|
// Set background to loading bitmap
|
||||||
imageView.setBackgroundDrawable(
|
imageView.setBackgroundDrawable(
|
||||||
@@ -369,7 +382,7 @@ public abstract class ImageWorker {
|
|||||||
imageView.setImageDrawable(td);
|
imageView.setImageDrawable(td);
|
||||||
td.startTransition(FADE_IN_TIME);
|
td.startTransition(FADE_IN_TIME);
|
||||||
} else {
|
} else {
|
||||||
imageView.setImageBitmap(bitmap);
|
imageView.setImageDrawable(drawable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.example.android.bitmapfun.util;
|
||||||
|
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.example.android.bitmapfun.BuildConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A BitmapDrawable that keeps track of whether it is being displayed or cached.
|
||||||
|
* When the drawable is no longer being displayed or cached,
|
||||||
|
* {@link Bitmap#recycle() recycle()} will be called on this drawable's bitmap.
|
||||||
|
*/
|
||||||
|
public class RecyclingBitmapDrawable extends BitmapDrawable {
|
||||||
|
|
||||||
|
static final String LOG_TAG = "CountingBitmapDrawable";
|
||||||
|
|
||||||
|
private int mCacheRefCount = 0;
|
||||||
|
private int mDisplayRefCount = 0;
|
||||||
|
|
||||||
|
private boolean mHasBeenDisplayed;
|
||||||
|
|
||||||
|
public RecyclingBitmapDrawable(Resources res, Bitmap bitmap) {
|
||||||
|
super(res, bitmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify the drawable that the displayed state has changed. Internally a
|
||||||
|
* count is kept so that the drawable knows when it is no longer being
|
||||||
|
* displayed.
|
||||||
|
*
|
||||||
|
* @param isDisplayed - Whether the drawable is being displayed or not
|
||||||
|
*/
|
||||||
|
public void setIsDisplayed(boolean isDisplayed) {
|
||||||
|
synchronized (this) {
|
||||||
|
if (isDisplayed) {
|
||||||
|
mDisplayRefCount++;
|
||||||
|
mHasBeenDisplayed = true;
|
||||||
|
} else {
|
||||||
|
mDisplayRefCount--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check to see if recycle() can be called
|
||||||
|
checkState();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify the drawable that the cache state has changed. Internally a count
|
||||||
|
* is kept so that the drawable knows when it is no longer being cached.
|
||||||
|
*
|
||||||
|
* @param isCached - Whether the drawable is being cached or not
|
||||||
|
*/
|
||||||
|
public void setIsCached(boolean isCached) {
|
||||||
|
synchronized (this) {
|
||||||
|
if (isCached) {
|
||||||
|
mCacheRefCount++;
|
||||||
|
} else {
|
||||||
|
mCacheRefCount--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check to see if recycle() can be called
|
||||||
|
checkState();
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void checkState() {
|
||||||
|
// If the drawable cache and display ref counts = 0, and this drawable
|
||||||
|
// has been displayed, then recycle
|
||||||
|
if (mCacheRefCount <= 0 && mDisplayRefCount <= 0 && mHasBeenDisplayed
|
||||||
|
&& hasValidBitmap()) {
|
||||||
|
if (BuildConfig.DEBUG) {
|
||||||
|
Log.d(LOG_TAG, "No longer being used or cached so recycling. "
|
||||||
|
+ toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
getBitmap().recycle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized boolean hasValidBitmap() {
|
||||||
|
Bitmap bitmap = getBitmap();
|
||||||
|
return bitmap != null && !bitmap.isRecycled();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user