Android AdMob causes memory leak?
Asked Answered
A

9

23

I've integrated AdMob v4.1.0 into my application and it seems to have caused a huge memory leak (pretty sure that it already happened on 4.0.4).

In order to isolate the problem I created a new project with a blank linear layout and added the AdView to it (this is actually a copy&paste from the sample code provided by AdMob). See my main.xml, MainActivity.java and manifest content:

main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:id="@+id/linearLayout">
<TextView  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="@string/hello"
    />
</LinearLayout>

MainActivity.java:

package AdsTry.main;

import com.google.ads.AdRequest;
import com.google.ads.AdSize;
import com.google.ads.AdView;

import android.app.Activity;
import android.os.Bundle;
import android.widget.LinearLayout;

public class MainActivity extends Activity {

    private final int AD_VIEW_ID = 1000000; 

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

         // Lookup R.layout.main
        LinearLayout layout = (LinearLayout)findViewById(R.id.linearLayout);

        // Create the adView
        // Please replace MY_BANNER_UNIT_ID with your AdMob Publisher ID
        AdView adView = new AdView(this, AdSize.BANNER, "MY_BANNER_UNIT_ID");
        adView.setId(AD_VIEW_ID);

        // Add the adView to it
        layout.addView(adView);

        // Initiate a generic request to load it with an ad
        AdRequest request = new AdRequest();

        adView.loadAd(request);           
    }

    @Override
    protected void onPause() {
        Log.i("AdsTry", "onPause");

        getAdView().stopLoading();

        super.onPause();
    }

    @Override
    protected void onDestroy() {
        Log.i("AdsTry", "onDestroy");

        getAdView().destroy();

        super.onDestroy();
    }

    private AdView getAdView()
    {
        return (AdView) findViewById(AD_VIEW_ID);
    }
}

manifest:

<application android:icon="@drawable/icon" android:label="@string/app_name">
    <activity android:name=".MainActivity"
              android:label="@string/app_name">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <!-- AdMobActivity definition -->
    <activity android:name="com.google.ads.AdActivity"
        android:configChanges="orientation|keyboard|keyboardHidden" />
</application>

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

And that's all the code I have.

Now, when running this application, I can see that both onPause and onDestory are called and the Activity is terminated, BUT the problem is that it will never be available for the GC since it causes the InputMethodManager to hold a reference to the Activity (See image taken from HPROF output after the activity was destroyed): MainActivity Merge shortest path to GC Roots

Once I remove the AdView-related code (and again, this is the ONLY code of this application) the problem goes away: Same HPROF output without using AdView

EDIT: Also tried removing ALL the code from onCreate and updated the main.xml to contain the following (still get the same result):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:ads="http://schemas.android.com/apk/lib/com.google.ads"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/linearLayout">
    <TextView  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="@string/hello"
    />
    <com.google.ads.AdView
    android:id="@+id/Ad"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    ads:adUnitId="MY_ID"
    ads:adSize="BANNER"
    ads:loadAdOnCreate="true"/>
</LinearLayout>

Any ideas ????

Anthracosilicosis answered 27/5, 2011 at 6:52 Comment(7)
More info is available at the following links: groups.google.com/forum/#!topic/google-admob-ads-sdk/… and groups.google.com/group/google-admob-ads-sdk/browse_thread/…Anthracosilicosis
Haven't tried it yet but I got a response on XDA saying that version 4.1.1 should fix that. forum.xda-developers.com/showthread.php?t=1077698Anthracosilicosis
I have been trying with 4.1.1 on my project and I still get an Activity leak. If I remove Admob the leak goes away. If I revert to old versions of Admob I still have an issue. If I call adView.onDestroy() in the onDestroy method it doesn't help. If I wait half an hour and press GC hundreds of times it doesn't help. Did you find ANY solution or work around? I'm totally stuck :(Misreckon
No solution worked for me. Ended up removing the ads. It's just not worth it...Anthracosilicosis
Im not sure if that helps but I created a bug on Android support site. Admob is currently held by Google so they should do something about it. I was pretty supprised that this bug wasn't there already. ISSUE 59627Kappel
Check my answer below for my solution to this problem. - Thanks!Eleneeleni
LoL, 6 years and it's still leakingReckford
O
7

I am using "play-services-ads:7.5.0" and it was not necesary to create de AdMobActivity. It worked by:

Creating adView dinamically

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

Removing all views from linearLayout on destroy and destroying adView

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

Unfortunatelly Interstitial still leaks

Ohare answered 18/6, 2015 at 20:51 Comment(0)
V
5

Here is my work around for this mess:

I've limited the memory leak, by using the same empty activity instance:

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);
        finish();
    }

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

Further Ad would be created using the AdMobActivity.AdMobMemoryLeakWorkAroundActivity.

You also need to add the activity to the manifest of course:

<activity
    android:launchMode="singleInstance"
    android:name="com.nu.art.software.android.modules.admob.AdMobActivity" />

This implementation goes against my beliefs regarding static references, which are not constants, but this implementation prevents the leak because only one activity instance is used to create all the ads, and so no more activity leaks, plus the fact that the activity is completely empty.

NOTE: You should call the startAdMobActivity method from the application main activity onCreate method.

Adam.

UPDATE

This solution works only if you create the ad dynamically, and add it to the layout with code... and don't forget to destroy it in the Activity.onDestroy().

Vapid answered 3/12, 2011 at 1:40 Comment(5)
Your post looks very helpful but I'm having trouble getting started using it. My project currently declares com.google.ads.AdView in each activities layout XML so I don't dynamically generate any ads or adviews in code. I don't see how your activity ties into the admob code. Can you post some samples on how to instantiate the activity and load an ad? Thanks!Strum
If anyone is having difficulty following TacB0sS instructions like I was, please see https://mcmap.net/q/485145/-admob-memory-leak-avoiding-by-using-empty-activity/684893. I've broken it down for the uninitiated of us.Strum
I tried this solution. And it does not leak my activity any more but now, when I click on any ad, it does not open that link. However if I pass current activity instance to AdView constructor, it works fine.Druid
I'm surprised that after all this time... (year and a half) they didn't fix this darn issue... and yes, you need to instantiate the AdActivity so there would be no more leaks, but in order to show your ad you need to pass the current activity!Vapid
I don't see why not, as long as you add the adview dynamically, see @Strum 's answerVapid
O
3

One way to fix this problem is to create one single instance of adview using your base activity of your application. Add this adview to any activity you want, but remember to remove it in the activities on destroy method.

This way the adview only leaks the base activity, which you almost never should have more than one instance of.

Example:

@Override
public void onCreate(final Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   this.setContentView(R.layout.venue);
   mainLayout = (LinearLayout) findViewById(R.id.venueLayout);
   adview = StaticStateClass.getAdview();

   AdRequest request = new AdRequest();
   request.addKeyword(name);
   mainLayout.addView(adview);
   adview.loadAd(request);
}

@Override
public void onDestroy() {
   mainLayout.removeView(adview);
   super.onDestroy();
}
Oof answered 11/7, 2011 at 15:10 Comment(1)
If you have the time, you can use reflections to manually hack apart the adview object and set and references of your context to null. I usually don't do this unless I have no choice.Oof
L
2

I found out that calling:-

mAdView.destroy(); 

before leaving the activity solves the leak for me.

Here is how I declare the banner ads in xml:-

  <com.google.android.gms.ads.AdView
            android:id="@+id/adView2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal|bottom"
            ads:adSize="BANNER"
            ads:adUnitId="@string/banner_ad_unit_id_2" />

Get a reference to admob banner in onCreate():-

mAdView = (AdView) findViewById(R.id.adView2);
AdRequest banneradRequest = new AdRequest.Builder().build();

mAdView.loadAd(banneradRequest);

and destroy it in ondestroy() of the activity:-

@Override
protected void onDestroy() {
    super.onDestroy();
    mAdView.destroy();
}
Lately answered 1/3, 2015 at 11:10 Comment(2)
super.onDestroy(); should be called after adView.destroy() right?Prescriptible
yeah super.onDestroy() should call afterVenturesome
A
1

I'm facing the same issue. The only one thing helped me - its System.exit(0). I don't like it but it is the only way i've found.
AdMob just stays in RAM 4ever and don't let my app to finish properly. When i restart my app OS just raises that undead app and soon it causes the out of memory exception.
My AdMob support forum topic - no support so far. Looks like no1 cares.

Arly answered 17/8, 2011 at 9:24 Comment(0)
E
0

Another answer that addresses this issue, even though it is a pain to do so, is to follow the following steps.

  • unpersist all data that's needed from sharedPreferences or any other types of on storage persistence,
    store those values in local variables/objects

  • WIPE ALL OF YOUR APP DATA

  • re-persist everything

You could start a service as your app is closing to do this. This would prevent data from getting really big due to the memory leak. Nasty way to address it, but I have tried it and it works.

Eleneeleni answered 3/10, 2014 at 23:42 Comment(0)
A
0

Create a AdView programmatically and pass the AdView to a any ViewGroup Like this;

MobileAds.initialize(applicationContext)
adView = AdView(applicationContext)
binding.adViewContainer.addView(adView)
adView.adSize = getAdSize(binding.adViewContainer)
adView.adUnitId = "ca-app-pub-3940256099942544/6300978111"
val adRequest = AdRequest.Builder().build()
adView.loadAd(adRequest)

Note: Use applicationContext instead of activity or fragment context to prevent memory leak

I've use view binding, test ads, framelayout and adaptive banner. I got the algorithm for getting the max width display here: https://developers.google.com/admob/android/banner/adaptive?hl=en-US#sample_code

override fun onDestroy() {
    adView.destroy()
    super.onDestroy()
    binding.adViewContainer.removeView(adView)
}
Ardisardisj answered 13/8, 2021 at 1:1 Comment(0)
R
-1

Donot merge your UI with Admob, You can run another thread to get ad from Admob. Also you can refresh the ad at particular interval of time. That will be easier for you to debug where the memory issue may be.

Thanks Deepak

Readytowear answered 27/5, 2011 at 7:25 Comment(3)
Thanks for your answer. What do you mean by having AdMob on a different thread ? do you mean that the code above from the onCreate should run in a different thread ? How will that work (Isn't it supposed to run on the UI thread ?)Anthracosilicosis
I want to say create a separate thread where in run() method write the code to get ad from Admob. make a start of that thread from this main class. keep away your UI from admob thread.Readytowear
I can't access the AdView view from outside the UI thread (I'm getting an exception). I've also tried removing ALL the code from onCreate and update the main.xml (See edit to the question). still get the same results.Anthracosilicosis
A
-2

Basically, this isn't really an issue. What's happening is your activity is receiving onDestroy, doing its cleanup, and then waiting around for a GC run. When the GC happens, it sees this old context lying around and cleans up all the underlying gunk, but it doesn't appear to clear the Activity in that pass--this is why your "leaked" Activity has such a small footprint: it's basically a shell. On the next GC pass, it will be cleaned up.

You can see this yourself by firing up your activity, backing out (to trigger onDestroy), going back into the process, firing a GC and an hprof dump, waiting a bit, then firing another GC and hprof dump. In the first dump--in my testing at least--I saw results similar to yours: an extra activity lying around with a very small memory footprint. In the second dump, I saw only the currently running activity.

Appropriate answered 2/6, 2011 at 22:44 Comment(1)
I've tried executing GC several times, waited a few minutes and ran it again and still the same results...Anthracosilicosis

© 2022 - 2024 — McMap. All rights reserved.