recuperados perdida memoria marte guia fragmento destiny datos alijo android memory-leaks leakcanary

android - marte - fragmento de memoria perdida



Pérdida de memoria en el fragmento (4)

Estoy usando la biblioteca LeakCanary para monitorear las fugas de memoria en mi aplicación. Recibí esta pérdida de memoria y no estoy seguro de cómo rastrear qué lo está causando.

05-09 09:32:14.731 28497-31220/? D/LeakCanary﹕ In com.etiennelawlor.minesweeper:0.0.21:21. * com.etiennelawlor.minesweeper.fragments.MinesweeperFragment has leaked: * GC ROOT com.google.android.gms.games.internal.GamesClientImpl$PopupLocationInfoBinderCallbacks.zzahO * references com.google.android.gms.games.internal.PopupManager$PopupManagerHCMR1.zzajo * references com.google.android.gms.games.internal.GamesClientImpl.mContext * references com.etiennelawlor.minesweeper.activities.MinesweeperActivity.mFragments * references android.app.FragmentManagerImpl.mAdded * references java.util.ArrayList.array * references array java.lang.Object[].[0] * leaks com.etiennelawlor.minesweeper.fragments.MinesweeperFragment instance * Reference Key: 2f367393-6dfd-4797-8d85-7ac52c431d07 * Device: LGE google Nexus 5 hammerhead * Android Version: 5.1 API: 22 * Durations: watch=5015ms, gc=141ms, heap dump=1978ms, analysis=23484ms

Este es mi repositorio: https://github.com/lawloretienne/Minesweeper

Esto parece ser un esquivo. Configuré una Interface para comunicarme entre un Fragment y una Activity . Configuré esta variable de Interface de onAttach() en onAttach() luego me di cuenta de que no la estaba anulando en onDetach() . Arreglé ese problema pero todavía tengo una pérdida de memoria. ¿Algunas ideas?

Actualizar

Deshabilité la observación de fugas de Fragment , y aún recibo una notificación sobre la fuga de actividad con el siguiente rastreo de fugas:

05-09 17:07:33.074 12934-14824/? D/LeakCanary﹕ In com.etiennelawlor.minesweeper:0.0.21:21. * com.etiennelawlor.minesweeper.activities.MinesweeperActivity has leaked: * GC ROOT com.google.android.gms.games.internal.GamesClientImpl$PopupLocationInfoBinderCallbacks.zzahO * references com.google.android.gms.games.internal.PopupManager$PopupManagerHCMR1.zzajo * references com.google.android.gms.games.internal.GamesClientImpl.mContext * leaks com.etiennelawlor.minesweeper.activities.MinesweeperActivity instance * Reference Key: f4d06830-0e16-43a2-9750-7e2cb77ae24d * Device: LGE google Nexus 5 hammerhead * Android Version: 5.1 API: 22 * Durations: watch=5016ms, gc=164ms, heap dump=3430ms, analysis=39535ms


En mi caso, tuve el siguiente código:

googleApiClient = new GoogleApiClient.Builder(activity.getApplicationContext()) .addConnectionCallbacks(this) .addOnConnectionFailedListener(this) .addApi(Games.API).addScope(Games.SCOPE_GAMES) .build();

El problema fueron las llamadas addConnectionCallbacks(this) y addConnectionCallbacks(this) . La clase a la que hace referencia this era mantener una referencia a una actividad, y como GoogleApiClient no deja de lado las referencias a las devoluciones de llamada / escucha de la conexión, esto resultó en una pérdida de memoria.

Mi solución fue registrar / anular el registro de las devoluciones de llamada a medida que el googleApiClient conecta / desconecta:

public void connect() { mGoogleApiClient.registerConnectionCallbacks(this); mGoogleApiClient.registerConnectionFailedListener(this); mGoogleApiClient.connect(); } public void disconnect() { if (mGoogleApiClient.isConnected()) { mGoogleApiClient.disconnect(); mGoogleApiClient.unregisterConnectionCallbacks(this); mGoogleApiClient.unregisterConnectionFailedListener(this); } }


La documentation indica que es seguro llamar a connect() incluso si el estado es "conectado" o "conectado". También indica que puede llamar de forma segura a disconnect() sin importar cuál sea el estado de la conexión. Por lo tanto, eliminaría las declaraciones "if" alrededor de las llamadas para connect() y disconnect() . Sin embargo, dudo que eso haga que esta "fuga" desaparezca.

Está claro que GamesClientImpl está almacenando una referencia a su Activity como Context . Me imagino que esto está ocurriendo en la construcción del GoogleApiClient que sucede cuando llama a GoogleApiClient.Builder.build() . Me parece un error que la instancia de GoogleApiClient siga existiendo después de que su Activity haya terminado. Sin embargo, si se supone que debes llamar a connect() en onStart() y a disconnect() en onStop() eso parece dar a entender que puedes reutilizar la conexión (ya que onStart() y onStop() pueden llamarse repetidamente). Para que esto funcione, GoogleApiClient debe mantener la referencia a su Context incluso después de haber llamado a disconnect() .

Puede intentar usar el contexto de la aplicación global en lugar del contexto de la Activity cuando cree el GoogleApiClient , ya que el contexto de la aplicación global perdura (hasta que GoogleApiClient el proceso). Esto debería hacer que su "fuga" desaparezca:

// Create the Google Api Client with access to Plus and Games mGoogleApiClient = new GoogleApiClient.Builder(getApplicationContext()) .addConnectionCallbacks(this) .addOnConnectionFailedListener(this) .addApi(Plus.API).addScope(Plus.SCOPE_PLUS_LOGIN) .addApi(Games.API).addScope(Games.SCOPE_GAMES) .build();


No debe confiar en la ejecución de devolución de llamada onDestroy() , en algunos casos es posible que no se llame . Una solución más sólida es poner su código de registro / onResume() registro dentro de onResume() / onPause() .

Lo mismo ocurre (por otra razón, por supuesto) para Fragment''s onDetach() , mueva su código sensible a onStop() o onPause() .


05-27 13:15:04.478 24415-25236/com.package D/LeakCanary﹕ In com.package:0.0.52-dev:202. * com.package.launcher.LauncherActivity has leaked: * GC ROOT com.google.android.gms.ads.internal.request.q.a * references com.google.android.gms.ads.internal.request.m.d * references com.google.android.gms.ads.internal.request.c.a * references com.google.android.gms.ads.internal.j.b * references com.google.android.gms.ads.internal.aa.f * references com.google.android.gms.ads.internal.ab.mParent * references com.google.android.gms.ads.doubleclick.PublisherAdView.mParent * references android.widget.FrameLayout.mContext * leaks com.package.launcher.LauncherActivity instance * Reference Key: 9ba3c5ea-2888-4677-9cfa-ebf38444c994 * Device: LGE google Nexus 5 hammerhead * Android Version: 5.1.1 API: 22 * Durations: watch=5128ms, gc=150ms, heap dump=5149ms, analysis=29741ms

Estaba usando la biblioteca de anuncios de GMS y hubo una fuga similar. Así que arreglé el caso anterior manejando onDestroyView () de mi fragmento.

@Override public void onDestroyView() { if (mAdView != null) { ViewParent parent = mAdView.getParent(); if (parent != null && parent instanceof ViewGroup) { ((ViewGroup) parent).removeView(mAdView); } } super.onDestroyView(); }

Así que aquí básicamente estoy eliminando mi PublisherAdView de su padre en onDestroyView ().

Además, tenga en cuenta que tuve que usar el contexto de la aplicación cuando creé PublisherAdView, de lo contrario obtendría la siguiente fuga:

05-27 13:59:23.684 10041-11496/com.package D/LeakCanary﹕ In com.package:0.0.52-dev:202. * com.package.launcher.LauncherActivity has leaked: * GC ROOT com.google.android.gms.ads.internal.request.q.a * references com.google.android.gms.ads.internal.request.m.b * leaks com.package.launcher.LauncherActivity instance * Reference Key: 5acaa61a-ea04-430a-b405-b734216e7e80 * Device: LGE google Nexus 5 hammerhead * Android Version: 5.1.1 API: 22 * Durations: watch=7275ms, gc=138ms, heap dump=5260ms, analysis=22447ms

No estoy seguro de si va a resolver la pregunta anterior directamente, pero espero que ayude.