android - Admob Memory Leak: evitando usar actividad vacía
memory-leaks (3)
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 cuestión Android AdMob provoca pérdida de memoria? y los subtítulos en los comentarios / respuestas. Me di cuenta de que el problema no es evidente en ICS, ya que el GC eventualmente 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.
Me gustaría seguir la solución proporcionada 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 del SDK de AdMob hasta donde yo sé.
Actualmente, estamos usando AdView en los diseños XML, por lo que no hacemos ningún esfuerzo dinámico con los anuncios en el código, como callAdload (). 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. Por lo tanto, mis dos preguntas son: ¿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 código?
Actualización 3/6:
Gracias Adam (TacB0sS) por 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 (...)". Fragmento de Stacktrace:
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 de inicializar su AdMobActivity y referenciarla al crear AdView? ¡Gracias de nuevo!
Actualización 2 3/6:
Calculé 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 utilicé:
Crea 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);
}
}
Agregue lo siguiente a su AndroidManifest.xml
<activity android:name="org.udroid.wordgame.AdMobActivity"
android:launchMode="singleInstance" />
Debe inicializar la AdMobActivity ficticia antes de intentar 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 invocó. No puede crearlo en la misma actividad en la que desea cargar anuncios, ya que debe estar totalmente inicializado a tiempo antes de usarlo. Lo inicializo en una actividad de pantalla de carga de bienvenida onCreate antes de que comience la actividad principal que contiene un anuncio:
// 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 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, cree una variable global para AdView:
AdView adView;
En nuestra aplicación, cargamos diferentes diseños cuando el teléfono gira. 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 llamar a adView.destroy () en el método activities onDestroy ():
@Override
protected void onDestroy() {
adView.destroy();
super.onDestroy();
}
Alguien más que lea esto, recuerde que esta es la solución de Adam (TacB0sS), no la mía. 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!
Estoy usando "play-services-ads: 7.5.0" y no fue necesario crear de AdMobActivity. Funcionó por:
Crear adView dinámicamente
mAdView = new AdView (getApplicationContext (), AdSize.BANNER, banner_ad_unit_id); mAdsContainer.addView (mAdView);
Eliminando todas las vistas de linearLayout en destruir y destruir adView
mAdView.setAdListener(null); mAdsContainer.removeAllViews(); mAdView.destroy();
Desafortunadamente, el intersticial aún tiene fugas
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.
Ravishi,
Su pregunta va al grano y no la he abordado en mi solución. Por lo que puedo decir, la solución que he encontrado funciona solo dinámicamente, 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 un poco más complicada que la que presenté, que involucra un marco de trabajo completo que construí en el marco de Android, donde la relación de AdMob con la aplicación es a través de un módulo intermedio, que coloca el anuncio de forma dinámica utilizando la instancia de actividad única.
Realmente dudo que puedas evitar la pérdida de memoria simplemente usando Android XML.
En cualquier caso, si está en el negocio de la pérdida de memoria, también debería comprobar el uso de AsyncTask ... también tiene su propio comportamiento de pérdida de memoria ... así que aquí está mi solución
la mejor de las suertes...
- ACTUALIZACIÓN - 10/07/14
Alguien acaba de subir mi respuesta, su propustorase que este problema aún existe, han pasado casi tres años desde mi respuesta original, y la gente todavía tiene pérdidas de memoria en sus aplicaciones debido a AdMob ... de Google ... que hizo Android. ..
De todos modos, solo quería agregar que probablemente debas establecer el tema de AdMobActivity en transparente, evitaría el parpadeo.
- ACTUALIZACIÓN - 28/02/16
Cuatro años...
- ACTUALIZACIÓN - 03/09/17
Cinco años ... Alguien en Google por favor despierte, y contrate a un mono real para hacer el trabajo :)
Adán.