Face à la même et grâce à @CommonsWare pour la discussion.
Affichage de la solution complète ici, donc il aide à plus de gens à venir ici pour la même question.
Les modifications et Commentaires sont les bienvenus. Cheers
When should I recycle a bitmap using LRUCache?
Précisément lorsque votre image est ni dans le cache et ni se référencés à partir de toute ImageView.
Pour maintenir le nombre de références de bitmap, nous devons étendre la BitmapDrawable classe et ajouter
les attributs de référence pour eux.
Cette android échantillon a la réponse c'est exactement ça.
DisplayingBitmaps.zip
Nous allons obtenir les détails et le code ci-dessous.
(don't recycle the bitmaps in the entryRemoved() call).
Pas exactement.
Dans entryRemoved délégué vérifier si l'image est toujours référencé à partir de tout ImageView.
Si pas. Recycler il lui-même.
Et inversement, ce qui est mentionné dans la accepté de répondre que lorsque la vue est sur le point d'obtenir réutilisés ou de se faire plaquer vérifier son image bitmap (précédent bitmap si la vue est réutilisée) est dans le cache. Si c'est là le laisser seul, le reste de la recycler.
La clé ici est que nous devons faire vérifier à la fois les lieux de savoir si nous pouvons recycler bitmap ou pas.
Je vais vous expliquer mon cas précis où je suis à l'aide de LruCache de tenir des bitmaps pour moi.
Et de les afficher dans la liste. Et l'appel à recycler sur les bitmaps lorsqu'il ne sont plus en cours d'utilisation.
RecyclingBitmapDrawable.java et RecyclingImageView.java de l'exemple mentionné ci-dessus sont les principales pièces dont nous avons besoin ici. Ils sont la manipulation des choses magnifiques.
Leur setIsCached et setIsDisplayed méthodes sont en train de faire ce dont nous avons besoin.
Le Code peut être trouvé dans l'échantillon lien mentionné ci-dessus. Mais aussi poster le code complet du fichier dans le fond de la réponse dans le cas où à l'avenir, le lien va vers le bas ou changé. Fait une petite modification de l'annulation de la setImageResource également de vérifier l'état de la précédente image.
- - - - Voici le code pour vous ---
Si votre LruCache gestionnaire devrait ressembler à quelque chose comme ça.
LruCacheManager.java
package com.example.cache;
import android.os.Build;
import android.support.v4.util.LruCache;
public class LruCacheManager {
private LruCache<String, RecyclingBitmapDrawable> mMemoryCache;
private static LruCacheManager instance;
public static LruCacheManager getInstance() {
if(instance == null) {
instance = new LruCacheManager();
instance.init();
}
return instance;
}
private void init() {
// We are declaring a cache of 6Mb for our use.
// You need to calculate this on the basis of your need
mMemoryCache = new LruCache<String, RecyclingBitmapDrawable>(6 * 1024 * 1024) {
@Override
protected int sizeOf(String key, RecyclingBitmapDrawable bitmapDrawable) {
// The cache size will be measured in kilobytes rather than
// number of items.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {
return bitmapDrawable.getBitmap().getByteCount() ;
} else {
return bitmapDrawable.getBitmap().getRowBytes() * bitmapDrawable.getBitmap().getHeight();
}
}
@Override
protected void entryRemoved(boolean evicted, String key, RecyclingBitmapDrawable oldValue, RecyclingBitmapDrawable newValue) {
super.entryRemoved(evicted, key, oldValue, newValue);
oldValue.setIsCached(false);
}
};
}
public void addBitmapToMemoryCache(String key, RecyclingBitmapDrawable bitmapDrawable) {
if (getBitmapFromMemCache(key) == null) {
// The removed entry is a recycling drawable, so notify it
// that it has been added into the memory cache
bitmapDrawable.setIsCached(true);
mMemoryCache.put(key, bitmapDrawable);
}
}
public RecyclingBitmapDrawable getBitmapFromMemCache(String key) {
return mMemoryCache.get(key);
}
public void clear() {
mMemoryCache.evictAll();
}
}
Et votre getView() de la ListView/GridView adaptateur doit paraître normal, comme d'habitude.
Lorsque vous créez une nouvelle image sur ImageView à l'aide de setImageDrawable méthode.
Son interne de vérifier le nombre de références à de précédentes bitmap et fera appel à recycler sur l'interne, si ce n'est dans lrucache.
@Override
public View getView(int position, View convertView, ViewGroup parent) {
RecyclingImageView imageView;
if (convertView == null) { // if it's not recycled, initialize some attributes
imageView = new RecyclingImageView(getActivity());
imageView.setLayoutParams(new GridView.LayoutParams(
GridView.LayoutParams.WRAP_CONTENT,
GridView.LayoutParams.WRAP_CONTENT));
imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
imageView.setPadding(5, 5, 5, 5);
} else {
imageView = (RecyclingImageView) convertView;
}
MyDataObject dataItem = (MyDataObject) getItem(position);
RecyclingBitmapDrawable image = lruCacheManager.getBitmapFromMemCache(dataItem.getId());
if(image != null) {
// This internally is checking reference count on previous bitmap it used.
imageView.setImageDrawable(image);
} else {
// You have to implement this method as per your code structure.
// But it basically doing is preparing bitmap in the background
// and adding that to LruCache.
// Also it is setting the empty view till bitmap gets loaded.
// once loaded it just need to call notifyDataSetChanged of adapter.
loadImage(dataItem.getId(), R.drawable.empty_view);
}
return imageView;
}
Ici est votre RecyclingImageView.java
/*
* 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.cache;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.util.AttributeSet;
import android.widget.ImageView;
/**
* 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);
}
/**
* @see android.widget.ImageView#setImageResource(android.graphics.drawable.Drawable)
*/
@Override
public void setImageResource(int resId) {
// Keep hold of previous Drawable
final Drawable previousDrawable = getDrawable();
// Call super to set new Drawable
super.setImageResource(resId);
// 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);
}
}
}
}
Ici est votre RecyclingBitmapDrawable.java
/*
* 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.cache;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.util.Log;
/**
* A BitmapDrawable that keeps track of whether it is being displayed or cached.
* When the drawable is no longer being displayed or cached,
* {@link android.graphics.Bitmap#recycle() recycle()} will be called on this drawable's bitmap.
*/
public class RecyclingBitmapDrawable extends BitmapDrawable {
static final String 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) {
//BEGIN_INCLUDE(set_is_displayed)
synchronized (this) {
if (isDisplayed) {
mDisplayRefCount++;
mHasBeenDisplayed = true;
} else {
mDisplayRefCount--;
}
}
// Check to see if recycle() can be called
checkState();
//END_INCLUDE(set_is_displayed)
}
/**
* 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) {
//BEGIN_INCLUDE(set_is_cached)
synchronized (this) {
if (isCached) {
mCacheRefCount++;
} else {
mCacheRefCount--;
}
}
// Check to see if recycle() can be called
checkState();
//END_INCLUDE(set_is_cached)
}
private synchronized void checkState() {
//BEGIN_INCLUDE(check_state)
// If the drawable cache and display ref counts = 0, and this drawable
// has been displayed, then recycle
if (mCacheRefCount <= 0 && mDisplayRefCount <= 0 && mHasBeenDisplayed
&& hasValidBitmap()) {
Log.d(TAG, "No longer being used or cached so recycling. "
+ toString());
getBitmap().recycle();
}
//END_INCLUDE(check_state)
}
private synchronized boolean hasValidBitmap() {
Bitmap bitmap = getBitmap();
return bitmap != null && !bitmap.isRecycled();
}
}