Memory Leak After Upgrading Ads Library For Android

234 Views Asked by At

I have upgraded the ads library to the following today:

implementation 'com.google.android.gms:play-services-ads:21.5.0'

I'm using Interstitial Ads only.

Now, I'm getting the following memory leak as detected by Leak Canary.

I'm destroying the activity along with all related ads objects, such as setting them to null, listeners to null, setFullScreenContentCallback(null), etc.

┬───
│ GC Root: Input or output parameters in native code
│
├─ android.os.MessageQueue instance
│    Leaking: NO (MessageQueue#mQuitting is false)
│    HandlerThread: "ExoPlayerImplInternal:Handler"
│    ↓ MessageQueue[0]
│                  ~~~
├─ android.os.Message instance
│    Leaking: UNKNOWN
│    Retaining 2.5 MB in 605 objects
│    Message.what = 2
│    Message.when = 507477121 (661 ms after heap dump)
│    Message.obj = null
│    Message.callback = null
│    Message.target = instance @348531240 of android.os.Handler
│    ↓ Message.target
│              ~~~~~~
├─ android.os.Handler instance
│    Leaking: UNKNOWN
│    Retaining 2.5 MB in 604 objects
│    ↓ Handler.mCallback
│              ~~~~~~~~~
├─ com.google.android.gms.internal.ads.zzatb instance
│    Leaking: UNKNOWN
│    Retaining 2.5 MB in 603 objects
│    ↓ zzatb.zzm
│            ~~~
├─ com.google.android.gms.internal.ads.zzaup instance
│    Leaking: UNKNOWN
│    Retaining 22.1 kB in 81 objects
│    ↓ zzaup.zzb
│            ~~~
├─ com.google.android.gms.internal.ads.zzatz instance
│    Leaking: UNKNOWN
│    Retaining 16 B in 1 objects
│    ↓ zzatz.zzb
│            ~~~
├─ com.google.android.gms.internal.ads.zzclf instance
│    Leaking: UNKNOWN
│    Retaining 346 B in 10 objects
│    zzd instance of com.google.android.gms.internal.ads.zzcpc, wrapping
│    androidx.multidex.MultiDexApplication
│    ↓ zzclf.zzd
│            ~~~
├─ com.google.android.gms.internal.ads.zzcpc instance
│    Leaking: UNKNOWN
│    Retaining 24 B in 1 objects
│    zza instance of com.xxxxx.xxxxx.CreateCardsActivity with
│    mDestroyed = true
│    zzb instance of androidx.multidex.MultiDexApplication
│    zzc instance of com.xxxxx.xxxxx.CreateCardsActivity with
│    mDestroyed = true
│    mBase instance of androidx.multidex.MultiDexApplication
│    zzcpc wraps an Application context
│    ↓ zzcpc.zza
│            ~~~
╰→ com.xxxx.xxxxxx.CreateCardsActivity instance
​     Leaking: YES (ObjectWatcher was watching this because 
     xxxx.xxxxx.CreateCardsActivity received Activity#onDestroy() callback and
​     Activity#mDestroyed is true)
​     Retaining 8.4 kB in 204 objects
​     key = 433e4c0b-dd8e-4321-a496-8a5a5542d28e
​     watchDurationMillis = 428997
​     retainedDurationMillis = 423991
​     mApplication instance of androidx.multidex.MultiDexApplication
​     mBase instance of androidx.appcompat.view.ContextThemeWrapper

METADATA

Build.VERSION.SDK_INT: 28
Build.MANUFACTURER: BLU
LeakCanary version: 2.10
App process name: com.xxx.xxxx
Class count: 20438
Instance count: 386631
Primitive array count: 252937
Object array count: 46275
Thread count: 148
Heap total bytes: 39734117
Bitmap count: 52
Bitmap total bytes: 29096284
Large bitmap count: 0
Large bitmap total bytes: 0
Db 1: open /com.google.android.
datatransport.events
Db 2: closed google_app_measurement_local.db
Db 3: open androidx.work.workdb
Stats: LruCache[maxSize=3000,hits=182820,misses=342787,hitRate=34%]
RandomAccess[bytes=16217423,reads=342787,travel=94405937828,range=40656354,size=
56429010]
Analysis duration: 149315 ms

This is how I set the fullScreenContentCallback in OnCreate():

           FullScreenContentCallback fullScreenContentCallback = new FullScreenContentCallback() {
                @Override
                public void onAdDismissedFullScreenContent() {
                    mAdManagerInterstitialAd.setFullScreenContentCallback(null);

                    mAdManagerInterstitialAd = null;
                }
            };

This is how I load the ad:

AdManagerAdRequest adRequest = new AdManagerAdRequest.Builder().build();

            AdManagerInterstitialAd.load(this,getResources().getString(R.string.interstitial_ad_unit_id), adRequest,
                    new AdManagerInterstitialAdLoadCallback() {
                        @Override
                        public void onAdLoaded(@NonNull AdManagerInterstitialAd interstitialAd) {
                            
                            mAdManagerInterstitialAd = interstitialAd;
                            mAdManagerInterstitialAd.setFullScreenContentCallback(fullScreenContentCallback);
                            
                        }

                        @Override
                        public void onAdFailedToLoad(@NonNull LoadAdError loadAdError) {
                            // Handle the error                                
                            mAdManagerInterstitialAd = null;
                        }
                    

I also tried to load the ad using getApplicationContext() instead of "this" as suggested in other forums but it did not work.

Any ideas?

1

There are 1 best solutions below

0
Pablo Alfonso On

This is more likely a bug in play-services-ads.

My workaround: Move mAdManagerInterstitialAd from the activity to a singleton. Then invoke 'load' and 'show' from the activity making reference to the mAdManagerInterstitialAd stored in the singleton, using callbacks and listeners.