android - example - leakcanary last version
Canario de fugas, Recyclerview fugas mAdapter (4)
Decidí que era hora de aprender a usar Leak Canary para detectar fugas en mis aplicaciones y, como siempre lo hago, traté de implementarlo en mi proyecto para entender realmente cómo usar la herramienta. Implementarlo fue bastante fácil, lo difícil fue leer lo que la herramienta me está devolviendo. Tengo una vista de desplazamiento que parece acumular memoria en el administrador de memoria cuando me desplazo hacia arriba y hacia abajo (aunque no carga ningún dato nuevo), así que pensé que era un buen objeto candidato para rastrear fugas, este es el resultado:
Parece que v7.widget.RecyclerView está perdiendo el adaptador, y no mi aplicación. Pero eso no puede ser correcto ... ¿verdad?
Aquí está el código para el adaptador y la clase que lo usa: https://gist.github.com/feresr/a53c7b68145d6414c40ec70b3b842f1e
Comencé una recompensa por esta pregunta porque reapareció después de dos años en una aplicación completamente diferente.
En primer lugar, estoy haciendo referencia a este archivo .
Parece que v7.widget.RecyclerView está perdiendo el adaptador, y no mi aplicación. Pero eso no puede ser correcto ... ¿verdad?
En realidad, es su adaptador el que está perdiendo RecyclerView
(y queda bastante claro por el gráfico de traza y el título de la actividad LeakCanary). Sin embargo, no estoy seguro si es el RecyclerView "primario" o el anidado en el HourlyViewHolder, o ambos. Creo que los culpables son tus ViewHolders. Al convertirlos en clases internas no estáticas, les proporciona explícitamente la referencia a la clase de adaptador adjunto, y esto casi acopla directamente el adaptador con las vistas recicladas, ya que el elemento primario de cada itemView
en sus soportes es el propio RecyclerView.
Mi primera sugerencia para solucionar este problema sería desacoplar sus ViewHolders y su Adaptador convirtiéndolos en clases internas estáticas . De esa manera, no tienen una referencia al adaptador, por lo que su campo de contexto será inaccesible para ellos, y también es algo bueno, ya que las referencias de contexto se deben pasar y almacenar con moderación (también para evitar grandes pérdidas de memoria). Cuando necesite el contexto solo para obtener las cadenas, hágalo en otro lugar, por ejemplo, en el constructor del adaptador, pero no almacene el contexto como miembro. Finalmente, el DayForecastAdapter
parece peligroso: le pasas la misma instancia a cada HourlyViewHolder
, lo que parece un error.
Creo que arreglar el diseño y desacoplar estas clases debería eliminar esta pérdida de memoria
No puedo abrir su imagen y ver la fuga real, pero si define una variable local para su RecyclerView
en su Fragment
y establece la retainInstanceState
su estado de retainInstanceState
de fragmento, puede causar posibles fugas con la rotación.
Cuando use un Fragment
con retainInstance
, debe borrar todas las referencias de su interfaz de onDestroyView
en onDestroyView
@Override
public void onDestroyView() {
yourRecyclerView = null;
super.onDestroyView();
}
Aquí puede encontrar información detallada en este enlace: Fragmentos retenidos con IU y pérdidas de memoria
Pude arreglar esto al reemplazar RecyclerView. Ocurre porque RecyclerView nunca se anula el registro de AdapterDataObservable.
@Override protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (getAdapter() != null) {
setAdapter(null);
}
}
Si el adaptador tiene una onDestroyView
mayor a la del RecyclerView
, debe borrar la referencia del adaptador en onDestroyView
:
@Override
public void onDestroyView() {
recyclerView.setAdapter(null);
super.onDestroyView();
}
De lo contrario, el adaptador mantendrá una referencia al RecyclerView
que ya debería haber salido de la memoria.