Android Cómo reciclar el mapa de bits correctamente cuando se utiliza RecyclerView?
Como google's dijo que debemos llamar manualmente Bitmap.recycle () cuando el mapa de bits no utilizar debajo de Android 3.0 porque la memoria se mantienen en el montón nativo.
Así que podríamos tener un recuento de referencia para un mapa de bits y comprobar si es necesario reciclar el mapa de bits en ImageView's onDetachedFromWindow () cuando se utiliza ListView. Esta es la solución del proyecto de demostración de Google Bitmapfun (ImageFetcher).
- ViewHolder en RecyclerView.Adapter no es específico para la posición
- GetAdapterPosition () no devuelve la posición del elemento en RecyclerView
- Ordenar los elementos de una visita de reciclaje mediante arrastrar y soltar
- RecyclerView encabezado y pie de página
- Espacio Problema al crear el diseño del bus en la vista del reciclador Gridlayout manager
Pero cuando se utiliza RecyclerView, la conversión a menudo se separa y se adjunta. OnDetachedFromWindow () tal vez reciclar el mapa de bits, así que cuando se adjunta a los padres de nuevo el mapa de bits se ha reciclado.
¿Cómo lidiar con este problema? ¿Cuál es la forma correcta de reciclar bitmap debajo de Android 3.0 cuando se usa RecyclerView?
Esta es la demostración de Googls BitmapFun (ImageFetcher) de la solucación – ampliar ImageView:
@Override protected void onDetachedFromWindow() { // This has been detached from Window, so clear the drawable setImageDrawable(null); super.onDetachedFromWindow(); } @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); } 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); } } }
Y también en LruCache cuando se quita del caché de memoria:
@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);
El RecyclingBitmapDrawable es:
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()) { if (BuildConfig.DEBUG) { 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(); }
}
El punto importante está en OnDetachedFromWindow de ImageView: setImageDrawable (null) significa claro dibujable, puede hacer que el recuento actual de drawble = 0 y lo recicle! Esta solución funciona bien cuando se utiliza ListView porque onDetachedFromWindow sólo ocurre cuando se destruye la actividad raíz de ListView.
Pero el uso de Recyclering es diferente, este Reusing ConvertView's Mechanism no es el mismo que ListView. ImageView tal vez a menudo separado y no es el momento correcto para reciclar Bitmap!
- Recyclerview dolorosamente lento para cargar imágenes en caché de Picasso
- Custom GridlayoutManager para WRAP_CONTENT en un RecyclerView
- Efecto de ondulación de Android anulado por el estado seleccionado
- CardView layout_width = "match_parent" no coincide con el ancho de RecyclerView padre
- La barra de herramientas no se contrae con RecyclerView anidado
- RecyclerView con GridLayoutManager y Picasso mostrando imagen incorrecta
- Implementar un RecyclerView en un fragmento
- RecyclerView no se desplaza como se esperaba
- Android: ¿Cómo puedo configurar un tiempo de espera para sockets SSL para un bloqueo de lectura / escritura usando el ThreadSafeClientConnectionManager?
- Decoración de artículos de RecyclerView