studio recyclerview libreria java android android-recyclerview android-5.0-lollipop picasso

java - libreria - Recyclerview es dolorosamente lento para cargar imágenes en caché de Picasso



picasso android (8)

He implementado un RecyclerView que contiene principalmente imágenes que se están cargando a través de Picasso. Mi problema es que tan pronto como me desplazo hacia abajo o hacia arriba en la vista, la imagen del marcador de posición aparece durante aprox. un segundo antes de que sea reemplazado por la imagen real. Se desplaza muy suavemente, pero es prácticamente inutilizable. Esto sucede cada vez que algo se desplaza fuera de la pantalla.

Picasso claramente almacena en caché las imágenes en el disco, ya que se cargan más rápido que el tiempo de carga inicial, pero definitivamente se recargan instantáneamente desde el caché. ¿Qué implementé incorrectamente?

Mi código de adaptador:

public class ExploreAdapter extends RecyclerView.Adapter<ExploreAdapter.ExploreViewHolder> { private List<ExploreItem> exploreItemList; private Context mContext; private int deviceWidth = PrefUtils.getDeviceWidth(); public ExploreAdapter(Context context, List<ExploreItem> exploreItemList){ this.mContext = context; this.exploreItemList = exploreItemList; } @Override public int getItemCount(){ return exploreItemList.size(); } @Override public void onBindViewHolder(ExploreViewHolder exploreViewHolder, int i){ ExploreItem item = exploreItemList.get(i); exploreViewHolder.getvTitle().setText(item.getTitle()); exploreViewHolder.getvUsername().setText(item.getUsername()); exploreViewHolder.getvNumLikes().setText(item.getNbOfLikes()); Picasso.with(mContext).load(item.getMediaLocation().trim()) .resize(deviceWidth, deviceWidth) .centerCrop() .placeholder(R.drawable.profile_background) .into(exploreViewHolder.getvImage()); Picasso.with(mContext).load(item.getUserProfilePicUrl().trim()) .placeholder(R.drawable.profile_picture) .into(exploreViewHolder.getvProfilePic()); } @Override public ExploreViewHolder onCreateViewHolder(ViewGroup viewGroup, int i){ View itemView = LayoutInflater. from(viewGroup.getContext()). inflate(R.layout.explore_item, viewGroup, false); return new ExploreViewHolder(itemView); } public static class ExploreViewHolder extends RecyclerView.ViewHolder{ private TextView vTitle, vUsername, vNumLikes; private SquareImage vImage; private ImageView vProfilePic; public ExploreViewHolder(View v){ super(v); this.vTitle = (TextView) v.findViewById(R.id.explore_item_title); this.vUsername = (TextView) v.findViewById(R.id.explore_item_username); this.vNumLikes = (TextView) v.findViewById(R.id.explore_item_number_likes); this.vImage = (SquareImage) v.findViewById(R.id.explore_item_image); this.vProfilePic = (ImageView) v.findViewById(R.id.explore_item_profile_picture); } public TextView getvTitle() { return vTitle; } public TextView getvUsername() { return vUsername; } public ImageView getvProfilePic() { return vProfilePic; } public SquareImage getvImage() { return vImage; } public TextView getvNumLikes() { return vNumLikes; } public void setvImage(SquareImage vImage) { this.vImage = vImage; } public void setvNumLikes(TextView vNumLikes) { this.vNumLikes = vNumLikes; } public void setvProfilePic(ImageView vProfilePic) { this.vProfilePic = vProfilePic; } public void setvTitle(TextView vTitle) { this.vTitle = vTitle; } public void setvUsername(TextView vUsername) { this.vUsername = vUsername; } } }

Cualquier ayuda es apreciada.


Establecer los parámetros de recyclerView de la siguiente manera resolvió mi problema de tartamudeo:

recyclerView.setHasFixedSize(true); recyclerView.setItemViewCacheSize(20); recyclerView.setDrawingCacheEnabled(true); recyclerView.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);

Espero que ayude a alguien más también.


La mejor respuesta a esta pregunta es no usar la vista de imagen redonda, solo use la transformación con una vista de imagen simple porque la vista de imagen redonda la aplicación requiere mucha memoria, solo use la vista de imagen simple y configure la transformación con ella. Usa esta transformación


Me he encontrado con este problema recientemente, y sí, tienes razón. Picasso es una buena biblioteca para mostrar imágenes de la tarjeta sd, pero tiene algunos problemas con respecto a la memoria caché si está buscando imágenes en línea, por eso ve una imagen de marcador de posición mientras se desplaza. Intenté cosas diferentes y el problema no se resolvió.

Hay una biblioteca alternativa, es decir, "AQuery", que es muy buena para recuperar y almacenar en caché imágenes de la red.

http://code.google.com/p/android-query/

Las principales ventajas de AQuery / Android Query es que puede borrar el caché, sin retrasos durante el desplazamiento y bastante efectivo para almacenar imágenes en caché y no mostrar imágenes de marcador de posición durante el desplazamiento.

Resolví mi problema eliminando picaaso y usando Aquery. Es solo un reemplazo de una línea y ya ha terminado con su problema.

(new AQuery(mContext)).id(exploreViewHolder.getvProfilePic()).image(item.getUserProfilePicUrl().trim(), true, true, device_width, R.drawable.profile_background, aquery.getCachedImage(R.drawable.profile_background),0);

Además, puede cambiar fácilmente de picasso a AQuery ya que no hay tal cosa que esté disponible en picasso pero no en AQuery y la sintaxis es casi la misma. Por lo tanto, le recomendaría que use AQuery, hasta que picasso lo solucione.


Mezclé la respuesta de @ergunkocak y @Mangesh Ghotage, usando esto funciona muy bien:

recyclerView.setHasFixedSize(true); recyclerView.setItemViewCacheSize(20); recyclerView.setDrawingCacheEnabled(true);

Y esto en cada imagen:

Picasso.with(context) .load(file) .fit() .into(imageView);

Además, puede configurar Picasso para usar una sola instancia como esta:

Picasso.setSingletonInstance(picasso);

Y finalmente, habilitar los indicadores de caché le dará pistas sobre lo que está mal:

Picasso .with(context) .setIndicatorsEnabled(true);

Más acerca de Picasso


No puedo verificar la exactitud de esta solución, pero también estaba enfrentando este problema y lo abordé siguiendo esta idea:

@Override public void onBindViewHolder(ViewHolder viewHolder, final int i) { ... if(mImageView.getDrawable() == null) Picasso.with(context) .load(path) .error(errorResId) .tag(tag) .into(mImageView); ... }

Su objetivo es prohibir que Picasso cargue algo a menos que ImageView no tenga nada que mostrar.


Picasso almacena automáticamente en caché las imágenes en dos niveles:

  • caché de disco (esto NO está en picasso sino en la capa de red que usa picasso)
  • Memoria caché

Están bot inicializados con valores predeterminados que se adaptan a la mayoría de las aplicaciones.

Cuando la memoria caché se está volviendo demasiado grande, el mapa de bits usado más antiguo se elimina de la memoria caché (que de forma predeterminada es 1/7 del espacio de almacenamiento dinámico disponible).

Creo que lo que está sucediendo aquí es que está cerca del límite de memoria de la memoria caché y, por lo tanto, las imágenes se decodifican nuevamente cada vez que las necesita nuevamente.

Ver aquí: Android Picasso Configurar el tamaño de LruCache

Pero le aconsejo CONTRA aumentar el tamaño de la memoria caché a menos que realmente cambie el tamaño de las imágenes al tamaño real que usa.

Creo que el problema con su código es este:

.resize(deviceWidth, deviceWidth)

¿estás seguro de que realmente necesitas una imagen de ese tamaño? ¿Qué almacenas en PrefUtils? ¿Se actualiza cuando el dispositivo gira? (paisaje vs retrato)

Si necesita imágenes más pequeñas, intente cambiar el tamaño del tamaño real de la imagen que va a utilizar. Si no sabe (calculado en tiempo de ejecución con el diseño), haga una aproximación o escriba su código para obtener el tamaño real (generalmente escribo clases personalizadas que amplían ImageView para estas cosas).

Si realmente necesita las imágenes en ese tamaño, simplemente aumente el tamaño de la memoria caché (vea el enlace de arriba) y debería estar listo.


Use fit() .

Picasso.with(context) .load(file) .fit() .centerCrop() .into(imageView);

El fit() realidad cambia el tamaño de la imagen para que se ajuste a los límites de la imagen. Esto no carga la imagen completa y suaviza el desplazamiento.


simplemente use el atributo noPlaceholder .