9

I also tried using the ApplicationContext but it still leaks for some reason.

Found a similar post about the issue here AdActivity leak on AdMob (SDK 7.0) for Android but without an answer.

Also tried to set the adlistener and the ad to null in onDestroy() but without any luck and still leaks the activity.

My code called in onCreate()

private void refreshInterstitial(){
        mInterstitialAd = new InterstitialAd(this);
        mInterstitialAd.setAdUnitId("AD_ID");
        mInterstitialAd.loadAd(new AdRequest.Builder().addTestDevice(AdRequest.DEVICE_ID_EMULATOR).addTestDevice("877BCC97E130A0DC62B2E5770D854496").build());

        mInterstitialAd.setAdListener(new AdListener() {
            @Override
            public void onAdLoaded() {
                mInterstitialAd.show();
            }
            @Override
            public void onAdClosed() {
            }
        });
}

Leakcanary Leak Trace

 ┬───
    │ GC Root: Global variable in native code
    │
    ├─ mx instance
    │    Leaking: UNKNOWN
    │    ↓ mx.a
    │         ~
    ├─ com.google.android.gms.ads.internal.webview.w instance
    │    Leaking: UNKNOWN
    │    mContext instance of com.google.android.gms.ads.internal.webview.ay, not wrapping activity
    │    View#mParent is null
    │    View#mAttachInfo is null (view detached)
    │    View.mWindowAttachCount = 1
    │    ↓ w.a
    │        ~
    ├─ com.google.android.gms.ads.internal.webview.aa instance
    │    Leaking: YES (View detached and has parent)
    │    mContext instance of com.google.android.gms.ads.internal.webview.ay, not wrapping activity
    │    View#mParent is set
    │    View#mAttachInfo is null (view detached)
    │    View.mWindowAttachCount = 1
    │    ↓ aa.mListenerInfo
    ├─ android.view.View$ListenerInfo instance
    │    Leaking: YES (aa↑ is leaking)
    │    ↓ View$ListenerInfo.mOnClickListener
    ├─ com.google.android.gms.ads.nonagon.ad.webview.f instance
    │    Leaking: YES (aa↑ is leaking)
    │    ↓ f.a
    ├─ com.google.android.gms.ads.nonagon.ad.webview.l instance
    │    Leaking: YES (aa↑ is leaking)
    │    ↓ l.e
    ├─ com.google.android.gms.ads.nonagon.ad.event.bs instance
    │    Leaking: YES (aa↑ is leaking)
    │    ↓ bs.a
    ├─ java.util.HashMap instance
    │    Leaking: YES (aa↑ is leaking)
    │    ↓ HashMap.table
    ├─ java.util.HashMap$Node[] array
    │    Leaking: YES (aa↑ is leaking)
    │    ↓ HashMap$Node[].[1]
    ├─ java.util.HashMap$Node instance
    │    Leaking: YES (aa↑ is leaking)
    │    ↓ HashMap$Node.key
    ├─ com.google.android.gms.ads.nonagon.shim.k instance
    │    Leaking: YES (aa↑ is leaking)
    │    ↓ k.a
    ├─ com.google.android.gms.ads.internal.client.ae instance
    │    Leaking: YES (aa↑ is leaking)
    │    ↓ ae.a
    ├─ com.google.android.gms.internal.ads.zzuc instance
    │    Leaking: YES (aa↑ is leaking)
    │    ↓ zzuc.zzcbw
    ├─ com.test.Activity$1 instance
    │    Leaking: YES (aa↑ is leaking)
    │    Anonymous subclass of com.google.android.gms.ads.AdListener
    │    ↓ EqualizerActivity$1.this$0
    ╰→ com.test.Activity instance
    ​     Leaking: YES (ObjectWatcher was watching this because Activity received Activity#onDestroy() callback and Activity#mDestroyed is true)
    ​     key = 40a1eb8e-c9e6-4062-b5f7-053e642e812f
    ​     watchDurationMillis = 5288
    ​     retainedDurationMillis = 258
Vince VD
  • 1,506
  • 17
  • 38
  • 1
    Have you tried making `mInterstitialAd` a local variable instead? – Harry Timothy Feb 25 '20 at 11:19
  • @harrytmthy Why would that solve the problem? – Vince VD Feb 26 '20 at 16:55
  • Because in most cases memory leak happened when we keep a reference of a mutable object, where this reference is NOT lifecycle aware. When `AdListener` callback is called, somehow your `mInterstitialAd` is being leaked. That's why I am asking you to turn it into a local variable instead, and make it `final` to ensure its immutability. That way, you can prevent the memory leak. – Harry Timothy Feb 26 '20 at 17:40
  • And if that's the case, please let me know so I can write it as an answer and claim the bounty :) – Harry Timothy Feb 26 '20 at 17:40
  • @harrytmthy just tried it and it's still leaking – Vince VD Feb 26 '20 at 18:53
  • See my answer :) – Harry Timothy Feb 27 '20 at 02:21
  • happened with me too. Seems like bug from their side – Sahil Manchanda Feb 28 '20 at 11:10
  • @SahilManchanda That's what i thought, but why is no one reporting this. I can barely find anything about this here on stackoverflow and on their community page they don't answer my questions. – Vince VD Feb 28 '20 at 15:15
  • Have you browsed some of the issues on https://github.com/googleads/googleads-mobile-android-examples? Does [issue #83](https://github.com/googleads/googleads-mobile-android-examples/issues/83) or [issue #84](https://github.com/googleads/googleads-mobile-android-examples/issues/84) look similar? – kasptom Feb 29 '20 at 20:02
  • 1
    @kasptom yes except i'm just using a basic interstitial ad and not a rewarded video but i just looked at the comments and someone has the exact same problem on #84. – Vince VD Feb 29 '20 at 20:11

3 Answers3

2

According to InterstitialAd Docs:

A single InterstitialAd object can be used to request and display multiple interstitial ads over the course of an activity's lifespan, so you only need to construct it once.

After looking at your code again, I noticed you re-construct mInterstitialAd every time refreshInterstitial() method is called. But according to the docs above, you should only construct mInterstitialAd once during onCreate().

In your case, the main cause of the memory leak: you still have an active listener (which is bound to Activity lifespan), yet you reconstruct a new InterstitialAd instance with another listener.

So, the solution is to reuse InterstitialAd instance and its listener without re-assigning. I suggest to simplify your refreshInterstitial() method to this:

private void refreshInterstitial() {
    mInterstitialAd.loadAd(new AdRequest.Builder().addTestDevice(AdRequest.DEVICE_ID_EMULATOR).addTestDevice("877BCC97E130A0DC62B2E5770D854496").build());
}

Then put mInterstitialAd assignment to onCreate(). This solution is similar to the one you can find here.

Harry Timothy
  • 1,148
  • 8
  • 17
  • I don't understand this? My method refreshInterstitial() is the code that is called in onCreate() when i launch my activity so it is called once. Also without the adlistener the interstitial won't show up because i have to call mInterstitialAd.show() when the ad is loaded. – Vince VD Feb 27 '20 at 15:04
  • Now let me ask you: do you only call `refreshInterstitial()` only once in the whole Activity lifespan? Because if you call it more than once, with your code, memory leak will happen. – Harry Timothy Feb 27 '20 at 16:51
  • Once. I just put it inside a method because i have alot of code in my onCreate – Vince VD Feb 27 '20 at 17:13
  • Then can you update your question, show us what's inside your `onCreate()` so we can figure out the solution? Because the Leak Trace logged `Leaking: YES (ObjectWatcher was watching this because Activity received Activity#onDestroy() callback and Activity#mDestroyed is true)`. Which means there is a part in your code which triggers `onDestroy()` then re-create the Activity, making your `InterstitialAd` constructed more than once. – Harry Timothy Feb 28 '20 at 05:28
  • Thats because i go back to my previous activity, Activity is a second activity and not my mainactivity. So when i press the back button ObjectWatcher is destroyed and that's normal. – Vince VD Feb 28 '20 at 15:17
  • 1
    I think this is a bug on their side unfortunately. Even their github example which is from google itself has this leak. – Vince VD Feb 28 '20 at 15:18
2

This is working for me: (in ActivityMain()):

MobileAds.initialize(WeakReference(applicationContext).get()){}

in fragments:

adView= AdView(WeakReference(requireActivity().application).get())

LeakCanary shows:

==================================== HEAP ANALYSIS RESULT ==================================== 0 APPLICATION LEAKS.

JMSP
  • 21
  • 3
1

I was facing the same issue in interstitial ad and it is resolved by setting the FullScreenContentCallback to null in all of its overridden methods.

 interstitial.setFullScreenContentCallback(new FullScreenContentCallback() {
        @Override
        public void onAdDismissedFullScreenContent() {
            super.onAdDismissedFullScreenContent();
            
            interstitial.setFullScreenContentCallback(null);}}
  • I know my memory leak is related to this specific setting with the FullScreenContentCallback. However, I'm still getting memory leaks even after setting this to null. – Pablo Alfonso Mar 04 '23 at 18:56