2012-03-04 7 views
25

La nostra app viene colpita da una perdita di memoria. Ho scoperto che la causa principale è AdMob AdView che mantiene i riferimenti alle vecchie attività. Il problema è abbastanza ben documentato nella domanda Android AdMob causes memory leak? e il sottolinking nei commenti/risposte. Ho notato che il problema non è evidente in ICS in quanto il GC alla fine pulisce le WebViews con riferimenti alle attività. Tuttavia, il mio gingerbread di HTC EVO 3D non raccoglie mai le attività e, considerando il numero di report di chiusura forzata dovuti a errori OOM, il problema è molto diffuso per la nostra app.Perdita di memoria Admob - evitando utilizzando attività vuota

Vorrei seguire la soluzione fornita da TacB0sS, https://stackoverflow.com/a/8364820/684893. Ha suggerito di creare un'attività vuota e utilizzare la stessa attività per ogni AdMob AdView. La perdita sarebbe contenuta poiché AdView manterrà in vita solo un'attività vuota. Ha fornito il codice per l'attività stessa e come farvi riferimento, ma non riesco a integrarlo nella nostra app. Il suo codice non chiama mai nulla da AdMob SDK per quanto posso dire.

Attualmente stiamo utilizzando AdView nei layout XML in modo da non eseguire dinamicamente alcunché con gli annunci nel codice come call loadAd(). Tutti i nostri layout con pubblicità si basano sul fatto che l'annuncio sia nell'XML poiché sono disposti in relazione ad esso. Le mie due domande sono quindi, come posso implementare il codice TacB0sS e come posso mantenere le mie relazioni di layout XML se dobbiamo passare alla creazione dei layout XML nel codice?

Aggiornamento 3/6:

Grazie Adam (TacB0sS) per la risposta! Non ho alcun problema a passare alla creazione del codice Annuncio in, ma sto ancora avendo difficoltà a utilizzare l'attività fittizia durante la creazione di annunci. Il mio codice è attualmente:

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); 

Ho inserito questo codice nel metodo onCreate della mia attività iniziale. Prendo una forza vicino alla linea in cui creo l'AdView, "AdView adView = new AdView (...)". Stacktrace Snippet:

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 

Qual è il modo corretto di inizializzare l'AdMobActivity e farvi riferimento durante la creazione del AdView? Grazie ancora!

Aggiornamento 2: 3/6:

ho capito i miei problemi che creano l'attività. Ho completamente implementato la tua soluzione e la parte migliore è che lo risolve effettivamente la mia perdita di memoria. Dopo aver trascorso due settimane su questo problema, sono così felice che è stato risolto. Ecco i passi pieni che ho usato:

creare una nuova attività denominata 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); 
    } 
} 

Aggiungere il seguente al vostro AndroidManifest.xml

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

È necessario inizializzare l'AdMobActivity manichino prima di provare caricare qualsiasi annuncio. Questa attività non conterrà nulla. Verrà visualizzato per una frazione di secondo e poi chiuso, ritornando all'attività in cui è stato chiamato. Non è possibile crearlo nella stessa attività in cui si desidera caricare Ads poiché deve essere inizializzato completamente in tempo prima dell'utilizzo.Inizializzo in onCreate di un'attività schermata di caricamento iniziale prima che l'attività principale che contiene un annuncio inizia:

// 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); 
} 

Ora siete pronti per creare annunci nel codice. Aggiungi il seguente LinearLayout al tuo layout di attività XML. Allinea tutte le altre viste necessarie attorno a questo layout. L'AdView che creiamo in codice sarà posizionata all'interno di questa vista.

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

Nell'attività si desidera caricare un annuncio, creare una variabile globale per l'AdView:

AdView adView; 

Nella nostra applicazione, abbiamo caricare diversi layout durante la rotazione del telefono. Pertanto, chiamo il seguente codice ad ogni rotazione. Crea l'adView, se necessario, e lo aggiunge al 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 

Infine, assicuratevi di chiamare adView.destroy() nelle attività OnDestroy() metodo:

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

chiunque altro che legge questo, si ricorda che questa è la soluzione di Adamo (TacB0sS), non mio. Volevo solo fornire i dettagli completi dell'implementazione per renderlo più semplice da implementare per gli altri. Questo bug AdMob è un grosso problema per le app che eseguono pre-honeycomb e la soluzione di Adam è la cosa migliore che riesco a trovare per aggirarlo. E funziona!

+0

funziona come un fascino !! Grazie ad entrambi @ravishi e @ TacB0sS !! – Regis

+0

Ciao ravishi ... Sto avendo problemi con questa soluzione. Potete per favore aiutarmi. Questa è la mia domanda http://stackoverflow.com/questions/15583994/activity-does-not-launch-from-the-recent-activities-android –

+0

C'è un collegamento al codice completo @ravishi? –

risposta

17

Ravishi,

La tua domanda è al punto, e io non sono riusciti a risolvere nel mio soluzione. Per quanto posso dire la soluzione che ho trovato funziona solo dinamicamente, dove puoi scegliere la tua attività mentre chiami il sdk ...

Il motivo per cui il mio codice non ha un esempio di utilizzo, è perché la mia soluzione è un un po 'più complicato di quello che ho presentato, coinvolgendo un intero framework di wrapping che ho costruito intorno al framework Android, dove la relazione AdMob con l'applicazione avviene tramite un modulo intermedio, che colloca l'annuncio in modo dinamico utilizzando l'istanza di singola attività.

Dubito davvero che si possa evitare la perdita di memoria semplicemente usando l'XML di Android.

In ogni caso, se siete nel business perdita di memoria, si potrebbe anche verificare il vostro utilizzo AsyncTask ... Ha anche un proprio comportamento perdita di memoria ... ecco la mia solution

migliore di fortuna ...

- AGGIORNAMENTO - 07/10/14

qualcuno ha appena upvoted la mia risposta, la sua propustorase che questo problema esiste ancora, è stato quasi tre anni da quando la mia risposta originale, e le persone hanno ancora perdite di memoria nelle loro app a causa di AdMob ... di Google ... che ha reso Android ....

In ogni caso, volevo solo aggiungere che probabilmente dovresti impostare il tema di AdMobActivity su trasparente, eviterebbe lo sfarfallio.

- AGGIORNAMENTO - 28/02/16

Quattro anni ...

- AGGIORNAMENTO - 09/03/17

Cinque anni ... Qualcuno al Google si prega di svegliarsi, e noleggio vera scimmia per fare il lavoro :)

Adam.

+0

Grazie Adam! Si prega di consultare il mio post modificato per una domanda più diretta. Grazie per aver anche segnalato il problema AsyncTask. Per fortuna non ci sono ancora riuscito o, se lo avessi, non è stata una grossa perdita come AdMob. – ravishi

+0

Quale versione di AdMob usi? Hai chiamato prima o dopo il super.onCreate (...)? In quale attività installi l'AdView, l'attività fittizia o la vera attività dell'interfaccia utente. – TacB0sS

+1

Ho funzionato. Vedi le modifiche nella domanda originale per i dettagli. Grazie mille! – ravishi

1

Stavo vedendo questa stessa perdita con l'SDK 6.1.0, ma ero in grado di risolverlo chiamando destroy() sull'AdView in questione in onDestroy della mia attività. Penso che l'abbiano risolto. Destroy() sembra sbarazzarsi delle PhantomReferences che la WebView di AdView ha avuto per la mia attività che impediva all'attività di essere GC.

6

Sto utilizzando "play-services-ads: 7.5.0" e non era necessario creare de AdMobActivity. Ha funzionato da:

  • Creazione ADView dinamicamente

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

  • Rimozione di tutte le viste dalla LinearLayout su distruggere e distruggere ADView

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

Purtroppo interstiziale ancora perdite

+1

Grazie mille amico. Ho avuto una perdita di 'LeakCanary' che fa riferimento a' MainActivity -> reference ht.a -> static hk.o'.Questo mi ha aiutato a risolverlo, che era una perdita 'NativeExpressAdView' sul mio' GridLayoutManager'. Non stavo rimuovendo tutte le viste dal mio 'GridLayoutManager' o impostando il mio' setAdListener' su null. Google ha completamente perso questo aspetto nel suo tutorial 'NativeExpressAdView'. –