Di fronte lo stesso e grazie a @CommonsWare per la discussione. Pubblicare qui la soluzione completa in modo che aiuti più persone a venire qui per lo stesso problema. Le modifiche e i commenti sono benvenuti. Acclamazioni
When should I recycle a bitmap using LRUCache?
Proprio quando il bitmap non è né nella cache e non sempre si fa riferimento da qualsiasi ImageView.
Per mantenere il conteggio dei riferimenti della bitmap, è necessario estendere la classe BitmapDrawable e aggiungere ad essi gli attributi di riferimento .
Questo campione Android ha la risposta esattamente. DisplayingBitmaps.zip
otterremo al dettaglio e il codice di seguito.
(don't recycle the bitmaps in the entryRemoved() call).
Non esattamente.
Nella voce delegato rimosso verificare se Bitmap è ancora referenziato da qualsiasi ImageView. In caso contrario. Riciclalo lì da solo.
E viceversa, menzionato nella risposta accettata che quando la vista sta per essere riutilizzata o viene scaricata, controllare la sua bitmap (la bitmap precedente se la vista viene riutilizzata) è nella cache. Se è lì, lascialo da solo, altrimenti riciclalo.
La chiave qui è che è necessario verificare in entrambe le posizioni se è possibile riciclare o meno bitmap.
spiegherò il mio caso specifico in cui sto usando LRUCache di tenere bitmap per me. E visualizzandoli in ListView. E chiamando riciclare su bitmap quando non sono più in uso.
RecyclingBitmapDrawable.java e RecyclingImageView.java del campione di cui sopra sono i pezzi di nucleo dobbiamo qui. Stanno gestendo le cose magnificamente. Loro setIsCached e setIsDisplayed i metodi stanno facendo ciò di cui abbiamo bisogno.
Il codice può essere trovato nel link di esempio menzionato sopra. Ma anche la pubblicazione del codice completo del file nella parte inferiore della risposta nel caso in cui in futuro il collegamento si interrompe o venga modificato. È stata eseguita una piccola modifica per sovrascrivere setImageResource anche per verificare lo stato della bitmap precedente.
--- Qui va il codice per voi ---
Così il responsabile LRUCache dovrebbe essere simile a questa.
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();
}
}
E il vostro GetView() di ListView/adattatore GridView dovrebbe essere normale come al solito. Come quando si imposta una nuova immagine su ImageView usando il metodo setImageDrawable. Controlla internamente il conteggio dei riferimenti sulla bitmap precedente e chiamerà il riciclo su di esso internamente se non in 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;
}
Ecco il vostro 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);
}
}
}
}
Qui è la vostra 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();
}
}
Come stai controllando se il bitmap è ancora nella cache ? – LuxuryMode
È possibile verificare se l'immagine è memorizzata nella cache utilizzando ImageLoader come imageLoader.isCached (url, maxWidth, maxHeight); maxWidth e maxHeight possono essere 0 ... – 66CLSjY