2012-03-04 31 views
25

Nuestra aplicación está siendo golpeada por una fuga de memoria. Descubrí que la causa principal es AdMob AdView manteniendo referencias a actividades anteriores. El problema está bastante bien documentado en la pregunta Android AdMob causes memory leak? y los subtítulos en los comentarios/respuestas. Me di cuenta de que el problema no es evidente en ICS, ya que el GC finalmente limpia las WebView con referencias a actividades. Sin embargo, mi pan de jengibre running EVO 3D de HTC nunca recopila las actividades y teniendo en cuenta la cantidad de informes de cierre forzado debido a errores de OOM, el problema está muy extendido en nuestra aplicación.Admob Memory Leak - evitando el uso de la actividad vacía

Me gustaría seguir la solución provista por TacB0sS, https://stackoverflow.com/a/8364820/684893. Él sugirió crear una actividad vacía y usar esa misma actividad para cada AdView de AdMob. La fuga estaría contenida ya que AdView solo mantendrá viva esa actividad vacía. Proporcionó el código para la actividad en sí y cómo hacer referencia a ella, pero no entiendo cómo integrarla en nuestra aplicación. Su código nunca llama nada desde el SDK de AdMob hasta donde yo sé.

Actualmente, estamos utilizando AdView en los diseños XML, por lo que no hacemos ningún esfuerzo dinámico con los anuncios en el código, como por ejemplo, invocar loadAd(). Todos nuestros diseños con anuncios se basan en que el anuncio se encuentra en el XML, ya que están diseñados en relación con él. Mis dos preguntas son, entonces, ¿cómo implemento el código TacB0sS y cómo puedo conservar mis relaciones de diseño XML si tenemos que cambiar a la creación de diseños XML en el código?

actualización 3/6:

Gracias Adam (TacB0sS) para responder! No tengo problemas para pasar a crear el anuncio en el código, pero sigo teniendo dificultades para usar tu actividad ficticia al crear anuncios. Mi código actualmente es:

AdMobActivity adActivity = new AdMobActivity(); 
adActivity.startAdMobActivity(this); 

// Create an ad with the activity reference pointing to dummy activity 
AdView adView = new AdView(adActivity.AdMobMemoryLeakWorkAroundActivity, AdSize.IAB_BANNER, "myAdUnitID"); 

// Create an ad request. 
AdRequest adRequest = new AdRequest(); 

// add the ad to the layout and request it to be filled 
RelativeLayout root_main = (RelativeLayout) findViewById(R.id.root_main); 
root_main.addView(adView); 
adView.loadAd(adRequest); 

He colocado este código en el método onCreate de mi actividad inicial. Obtengo una fuerza cercana en la línea donde creo AdView, "AdView adView = new AdView (...)". StackTrace fragmento:

03-06 00:34:28.098 E/AndroidRuntime(16602): java.lang.RuntimeException: Unable to start activity ComponentInfo{org.udroid.wordgame/org.udroid.wordgame.MainMenu}: java.lang.NullPointerException 
03-06 00:34:28.098 E/AndroidRuntime(16602):  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1830) 
(...) 
03-06 00:34:28.098 E/AndroidRuntime(16602): Caused by: java.lang.NullPointerException 
03-06 00:34:28.098 E/AndroidRuntime(16602):  at android.content.ContextWrapper.getApplicationContext(ContextWrapper.java:100) 
03-06 00:34:28.098 E/AndroidRuntime(16602):  at com.google.ads.AdView.<init>(SourceFile:78) 
03-06 00:34:28.098 E/AndroidRuntime(16602):  at org.udroid.wordgame.MainMenu.onCreate**(MainMenu.java:71)** <- Line that creates the new AdView 

¿Cuál es la forma correcta para inicializar su AdMobActivity y hacer referencia a ella cuando se crea el AdView? ¡Gracias de nuevo!

Actualización 2 3/6:

me di cuenta de mis problemas al crear la actividad. Tengo su solución completamente implementada y la mejor parte es que realmente resuelve mi pérdida de memoria. Después de pasar dos semanas en este problema, estoy tan feliz de que se haya resuelto. Estos son los pasos completos que he utilizado:

crear una nueva actividad llamada AdMobActivity:

public final class AdMobActivity extends Activity { 

    public static AdMobActivity AdMobMemoryLeakWorkAroundActivity; 

    public AdMobActivity() { 
     super(); 
     if (AdMobMemoryLeakWorkAroundActivity != null) { 
      throw new IllegalStateException("This activity should be created only once during the entire application life"); 
     } 
     AdMobMemoryLeakWorkAroundActivity = this; 
    } 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     Log.i("CHAT", "in onCreate - AdMobActivity"); 
     finish(); 
    } 

    public static final void startAdMobActivity(Activity activity) { 
     Log.i("CHAT", "in startAdMobActivity"); 
     Intent i = new Intent(); 
     i.setComponent(new ComponentName(activity.getApplicationContext(), AdMobActivity.class)); 
     activity.startActivity(i); 
    } 
} 

Añadir lo siguiente a su AndroidManifest.xml

<activity android:name="org.udroid.wordgame.AdMobActivity" 
    android:launchMode="singleInstance" /> 

Es necesario inicializar el AdMobActivity maniquí antes de intentar para cargar cualquier anuncio Esta actividad no contendrá nada. Se mostrará durante una fracción de segundo y luego se cerrará, volviendo a la actividad en la que lo llamó. No puede crearlo en la misma actividad en la que desea cargar anuncios, ya que debe estar completamente inicializado a tiempo antes de usarlo.Me inicializarlo en onCreate de una actividad de la pantalla de bienvenida de carga antes de la actividad principal que contiene un anuncio comienza:

// Start the dummy admob activity. Don't try to start it twice or an exception will be thrown 
if (AdMobActivity.AdMobMemoryLeakWorkAroundActivity == null) { 
    Log.i("CHAT", "starting the AdMobActivity"); 
    AdMobActivity.startAdMobActivity(this); 
} 

Ahora ya está listo para crear anuncios en el código. Agregue el siguiente LinearLayout a su diseño de actividad XML. Alinee todas las demás vistas necesarias alrededor de este diseño. El AdView que creamos en el código se colocará dentro de esta vista.

<LinearLayout 
android:id="@+id/adviewLayout" 
android:layout_width="wrap_content" 
android:layout_height="wrap_content" 
android:layout_alignParentBottom="true" 
android:layout_centerHorizontal="true" /> 

En la actividad que desea cargar un anuncio, crear una variable global para AdView:

AdView adView; 

En nuestra aplicación, cargamos diferentes diseños al girar el teléfono. Por lo tanto, llamo al siguiente código en cada giro. Crea adView si es necesario y lo agrega a adviewLayout.

// DYNAMICALLY CREATE AD START 
    LinearLayout adviewLayout = (LinearLayout) findViewById(R.id.adviewLayout); 
    // Create an ad. 
    if (adView == null) { 
     adView = new AdView(AdMobActivity.AdMobMemoryLeakWorkAroundActivity, AdSize.BANNER, "<ADUNITID>"); 
     // Create an ad request. 
     AdRequest adRequest = new AdRequest(); 
     // Start loading the ad in the background. 
     adView.loadAd(adRequest); 
     // Add the AdView to the view hierarchy. The view will have no size until the ad is loaded. 
     adviewLayout.addView(adView); 
    } 
    else { 
     ((LinearLayout) adView.getParent()).removeAllViews(); 
     adviewLayout.addView(adView); 
     // Reload Ad if necessary. Loaded ads are lost when the activity is paused. 
     if (!adView.isReady() || !adView.isRefreshing()) { 
      AdRequest adRequest = new AdRequest(); 
      // Start loading the ad in the background. 
      adView.loadAd(adRequest); 
     } 
    } 
    // DYNAMICALLY CREATE AD END 

Por último, asegúrese de que usted llama adView.destroy() en las actividades OnDestroy() Método:

@Override 
protected void onDestroy() { 
    adView.destroy(); 
super.onDestroy(); 
} 

Cualquier otra persona que lee esto, por favor recuerde que esta es la solución de Adam (TacB0sS), no es mio. Solo quería proporcionar los detalles completos de implementación para que otros los implementen más fácilmente. Este error de AdMob es un gran problema para las aplicaciones que ejecutan pre-honeycomb y la solución de Adam es lo mejor que pude encontrar para eludirlo. ¡Y funciona!

+0

funciona como un encanto !! ¡Gracias a los dos @ravishi y @ TacB0sS !! – Regis

+0

Hola, ravishi ... Tengo problemas con esta solución. Podrías ayudarme. Esta es mi pregunta http://stackoverflow.com/questions/15583994/activity-does-not-launch-from-the-recent-activities-android –

+0

¿Hay un enlace al código completo @ravishi? –

Respuesta

17

Ravishi,

Su pregunta es hasta el punto, y que no se han ocupado en mi solución. Por lo que puedo decir, la solución que he encontrado funciona solo de forma dinámica, donde puedes elegir tu actividad mientras llamas al SDK ...

La razón por la que mi código no tiene un ejemplo de uso es porque mi solución es una un poco más complicado que el que presenté, que involucra un marco completo de envolvimiento que construí en el marco de Android, donde la relación de AdMob con la aplicación se realiza a través de un módulo intermedio, que coloca el anuncio dinámicamente utilizando la única instancia de actividad.

Realmente dudo que pueda evitar la pérdida de memoria simplemente usando el Android XML.

En cualquier caso, si usted está en el negocio de pérdida de memoria, puede ser que también echa un vistazo a su uso de AsyncTask ... También tiene su propio comportamiento pérdida de memoria ... así que aquí es mi solution

mejor de suerte ...

- ACTUALIZACIÓN - 07/10/14

Alguien acaba de upvoted mi respuesta, su propustorase que todavía existe este problema, se ha pasado casi tres años desde mi primera respuesta, y la gente todavía tienen pérdidas de memoria en sus aplicaciones debido a AdMob ... de Google ... que creó Android ....

De todos modos, solo quería agregar que probablemente debiera establecer el tema de AdMobActivity en transparente, evitaría el parpadeo.

- ACTUALIZACIÓN - 28/02/16

cuatro años ...

- ACTUALIZACIÓN - 09/03/17

Cinco años ... alguien en Google por favor, despierta, y contratar mono real para hacer el trabajo :)

Adán.

+0

Gracias Adam! Por favor vea mi publicación editada para una pregunta más directa. Gracias también por señalar el problema AsyncTask. Afortunadamente no lo he encontrado todavía o, si lo he hecho, no ha sido una gran filtración como AdMob. – ravishi

+0

¿Qué versión de AdMob usa? ¿Lo llamaste antes o después del super.onCreate (...)? En qué actividad crea una instancia de AdView, la actividad simulada o la actividad real de la interfaz de usuario. – TacB0sS

+1

Lo tengo trabajando. Ver ediciones en la pregunta original para más detalles. ¡Muchas gracias! – ravishi

1

Estaba viendo esta misma pérdida con el SDK 6.1.0, pero pude resolverlo llamando a destroy() en el AdView en cuestión en el Activity onDestroy. Creo que lo arreglaron. The destroy() parece deshacerse de las PhantomReferences que tenía WebView de AdView para mi actividad que impedía que la actividad fuera GC.

6

Estoy usando "play-services-ads: 7.5.0" y no fue necesario crear de AdMobActivity. Se trabajó por:

  • Creación adView dinámicamente

    mAdView = new AdView (getApplicationContext(), AdSize.BANNER, banner_ad_unit_id); mAdsContainer.addView (mAdView);

  • Extracción de todos los puntos de vista de LinearLayout en destruir y destruir adView

      mAdView.setAdListener(null); 
          mAdsContainer.removeAllViews(); 
          mAdView.destroy(); 
    

Desafortunadamente intersticial aun pierde

+1

Muchas gracias hombre. Tuve una fuga de 'LeakCanary' haciendo referencia a 'MainActivity -> referencias ht.a -> static hk.o'.Esto me ayudó a resolverlo, que era una pérdida 'NativeExpressAdView' en mi' GridLayoutManager'. No estaba eliminando todas las vistas de mi 'GridLayoutManager' ni estableciendo mi' setAdListener' en nulo. Google se perdió completamente esto en su tutorial 'NativeExpressAdView'. –