I have memory leaks regarding Interstitial
ads of AdMob
with AdActivity
object. Whenever an ad is shown, AdActivity
object count in the memory increments by 1. I inspect all changes via MAT
after explicit GC
calls. I use the latest versions of everything.
At first, I thought that this is related to how I implemented my UI or project, but creating a fresh and empty project shows the same leak.
This leak has also existed in the previous Admob SDK
(Google Play Services) and now it exists in version 7.0 too.
I see that people try to solve these kinds of issues by creating a SingleInstance
empty activity just to show and set as the context of the interstitial ads. I tried them all and they did not work for my case. Some did help but I even couldn't use it because of the flow of my app. launchMode
in Android has limitations and it does not help me on my case.
I already notified the AdMob team but even they fix it, it doesn't seem to happen in a short time as they have just released the version 7.0 SDK.
I do not understand how others do not report leaks like this. It cannot be a special case just for me as it happens on even samples or default templates. If anyone somehow solved this issue (including ugly reflection hacks) please share your experience. I have been working on this for months! Really!
AndroidManifest:
<uses-sdk
android:minSdkVersion="9"
android:targetSdkVersion="22" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:name="MyApplication"
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<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>
<activity
android:name="com.google.android.gms.ads.AdActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"
android:theme="@android:style/Theme.Translucent" />
</application>
MainActivity:
package com.example.leaktest1;
import com.google.android.gms.ads.AdListener;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.InterstitialAd;
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
private InterstitialAd interstitial=null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
interstitial= new InterstitialAd(getApplicationContext());
interstitial.setAdUnitId("YOUR-ADD-ID");
AdRequest adRequest2 = new AdRequest.Builder()
// .addTestDevice(AdRequest.DEVICE_ID_EMULATOR)
.build();
interstitial.setAdListener(new AdListener() {
@Override
public void onAdLoaded() {
super.onAdLoaded();
if(interstitial.isLoaded()){
interstitial.show();
}
}
});
interstitial.loadAd(adRequest2);
}
@Override
protected void onDestroy() {
if(interstitial!=null){
interstitial.setAdListener(null);
interstitial=null;
}
super.onDestroy();
}
}
Layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.leaktest1.MainActivity" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world" />
</RelativeLayout>
- Start the app. When the ad is shown, close it with backpress and hit back button again to exit app.
- App will still be in memory, but the activity is gone. Now touch the app icon to start the activity again, it will show the ad again, exit like you did before.
- Cause GC multiple times and get heap dump. You will see that there are 2 AdActivity objects (and also many other related objects). It will continue to grow according to the number of shown ads.
The following did not work too (it still leaks):
/*
interstitial.setAdListener(new AdListener() {
@Override
public void onAdLoaded() {
super.onAdLoaded();
}
});*/
interstitial.loadAd(adRequest2);
Runnable r=new Runnable() {
@Override
public void run() {
if(interstitial.isLoaded()){
interstitial.show();
}
}};
new Handler().postDelayed(r,10000);
And putting code inside a button did not work too (it still leaks):
Button b = new Button(this);
b.setText("Touch me");
b.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if(interstitial.isLoaded()){
interstitial.show();
}
}
});
ViewGroup v = (ViewGroup) this.findViewById(android.R.id.content);
v.addView(b);
Leak platform and exceptions: This leak happens on various devices from Samsung and Asus with various un-modded (original firmware) Androis systems from 2.3 to 4.4. It also happens on any setup of android simulator.
(This leak does not show up on rooted Cyanogenmod (Galaxy S3) Android 4.4.4)
UPDATE
The leak does not vanish if I use Activity
context instead of Application
context. It also causes Activity
leak too.
Activity
as opposed toApplicationContext()
to the constructor? I believe since you're passing in theApplicationContext()
, the ad may live for the lifetime of the application. Just a thought. What I mean is to replaceinterstitial= new InterstitialAd(getApplicationContext());
withinterstitial= new InterstitialAd(this);
– DrivenActivity context
instead ofApplicationContext
is always my favorite but it just causes additional leaks by also keeping my Activity. As long as AdActivity lives, some how it doesn't leave my Activity. After your message, I retried it and I can confirm my reply; so I update the question to reflect this situation too. – Bankinterstitial.destroy()
on youronDestroy()
? – Handcraftdestroy()
method forInterstitialAd
object in the SDK. – Bank