简体   繁体   中英

Rewarded ads memory leak admob

I am super new to android coding, please tell me if this memory leak is significant enough for me to get worried. And if so please suggest something i can do about it.

I am trying to use admob to show rewarded ads in my android app, but after implementing the standard rewarded ads. My app started suffering from performance issues when I investigated the problem it turn out that and RewardedAdCallback is referencing my MainActivity causing a memory leak. How can I fix this after searching for 3 three days I am completely lost

this is my MainActivity

package com.example.ads

import android.app.Activity
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.view.View
import androidx.annotation.NonNull
import com.google.android.gms.ads.AdError
import com.google.android.gms.ads.AdRequest
import com.google.android.gms.ads.LoadAdError
import com.google.android.gms.ads.MobileAds
import com.google.android.gms.ads.rewarded.RewardItem
import com.google.android.gms.ads.rewarded.RewardedAd
import com.google.android.gms.ads.rewarded.RewardedAdCallback
import com.google.android.gms.ads.rewarded.RewardedAdLoadCallback

class MainActivity : AppCompatActivity() {
    lateinit var rewardedAd: RewardedAd

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        MobileAds.initialize(applicationContext)

        rewardedAd = RewardedAd(this,
                "ca-app-pub-3940256099942544/5224354917")
        val adLoadCallback = object: RewardedAdLoadCallback() {
            override fun onRewardedAdLoaded() {
                // Ad successfully loaded.
            }
            override fun onRewardedAdFailedToLoad(adError: LoadAdError) {
                // Ad failed to load.
            }
        }
        rewardedAd.loadAd(AdRequest.Builder().build(), adLoadCallback)
    }

    fun showads(view:View){
        if (rewardedAd.isLoaded) {
            val activityContext: Activity = this@MainActivity
            val adCallback = object : RewardedAdCallback() {
                override fun onRewardedAdOpened() {
                    // Ad opened.
                }

                override fun onRewardedAdClosed() {
                    // Ad closed.
                    rewardedAd = createAndLoadRewardedAd()
                }

                override fun onUserEarnedReward(@NonNull reward: RewardItem) {
                    // User earned reward.
                }

                override fun onRewardedAdFailedToShow(adError: AdError) {
                    // Ad failed to display.
                }
            }
            rewardedAd.show(activityContext, adCallback)
        } else {
            Log.d("TAG", "The rewarded ad wasn't loaded yet.")
        }
    }
    fun createAndLoadRewardedAd(): RewardedAd {
        val rewardedAd = RewardedAd(this, "ca-app-pub-3940256099942544/5224354917")
        val adLoadCallback = object: RewardedAdLoadCallback() {
            override fun onRewardedAdLoaded() {
                // Ad successfully loaded.
            }
            override fun onRewardedAdFailedToLoad(adError: LoadAdError) {
                // Ad failed to load.
            }
        }
        rewardedAd.loadAd(AdRequest.Builder().build(), adLoadCallback)
        return rewardedAd
    }
    fun l(view:View){
        val intent = Intent(this,MainActivity2::class.java)
        startActivity(intent)
    }
}

this is my MainActivity2

package com.example.ads

import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View

class MainActivity2 : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main2)
    }
    fun goToSecond(view: View){
        val intent = Intent(this,MainActivity::class.java)
        startActivity(intent)

    }

}

the MainActivty has 2 buttons one for showing ads (onClick:showads()) another for starting the second activity(onClick:goToSecond()) and the MainActivity2 has 1 button that starts the first activity

steps to recreate the memory leak are:-

  1. start the app
  2. on the first activity press the button to go to second activity
  3. on the second activity press the button to go to first activity
  4. now when on the first activity press the button to show the rewarded ad
  5. when the rewarded ad is finished and you are back at the first activity press the back button now leak canary must find the memory leak

Below is the leaky canary report

My question is why is Google ads causing a memory leak

┬───
│ GC Root: Global variable in native code
│
├─ ns instance
│    Leaking: UNKNOWN
│    ↓ ns.a
│         ~
├─ com.google.android.gms.ads.internal.webview.x instance
│    Leaking: UNKNOWN
│    mContext instance of com.google.android.gms.ads.internal.webview.ax, not wrapping activity
│    View#mParent is null
│    View#mAttachInfo is null (view detached)
│    View.mWindowAttachCount = 1
│    ↓ x.a
│        ~
├─ com.google.android.gms.ads.internal.webview.ab instance
│    Leaking: YES (View detached and has parent)
│    mContext instance of com.google.android.gms.ads.internal.webview.ax, not wrapping activity
│    View#mParent is set
│    View#mAttachInfo is null (view detached)
│    View.mWindowAttachCount = 1
│    ↓ ab.mListenerInfo
├─ android.view.View$ListenerInfo instance
│    Leaking: YES (ab↑ is leaking)
│    ↓ View$ListenerInfo.mOnClickListener
├─ com.google.android.gms.ads.nonagon.ad.webview.f instance
│    Leaking: YES (ab↑ is leaking)
│    ↓ f.a
├─ com.google.android.gms.ads.nonagon.ad.webview.l instance
│    Leaking: YES (ab↑ is leaking)
│    ↓ l.d
├─ com.google.android.gms.ads.nonagon.ad.event.cz instance
│    Leaking: YES (ab↑ is leaking)
│    ↓ cz.b
├─ java.util.HashMap instance
│    Leaking: YES (ab↑ is leaking)
│    ↓ HashMap.table
├─ java.util.HashMap$Node[] array
│    Leaking: YES (ab↑ is leaking)
│    ↓ HashMap$Node[].[2]
├─ java.util.HashMap$Node instance
│    Leaking: YES (ab↑ is leaking)
│    ↓ HashMap$Node.key
├─ com.google.android.gms.ads.nonagon.ad.event.cd instance
│    Leaking: YES (ab↑ is leaking)
│    ↓ cd.g
├─ com.google.android.gms.ads.nonagon.slot.rewarded.u instance
│    Leaking: YES (ab↑ is leaking)
│    ↓ u.c
├─ java.util.concurrent.atomic.AtomicReference instance
│    Leaking: YES (ab↑ is leaking)
│    ↓ AtomicReference.value
├─ com.google.android.gms.ads.internal.rewarded.client.f instance
│    Leaking: YES (ab↑ is leaking)
│    ↓ f.a
├─ com.google.android.gms.internal.ads.zzauy instance
│    Leaking: YES (ab↑ is leaking)
│    ↓ zzauy.zzdvi
├─ com.example.logicpuzzle.MainActivity$giveHint$adCallback$1 instance
│    Leaking: YES (ab↑ is leaking)
│    Anonymous subclass of com.google.android.gms.ads.rewarded.RewardedAdCallback
│    ↓ MainActivity$giveHint$adCallback$1.this$0
╰→ com.example.logicpuzzle.MainActivity instance
​     Leaking: YES (ObjectWatcher was watching this because com.example.logicpuzzle.MainActivity received Activity#onDestroy() callback and Activity#mDestroyed is true)
​     key = ab343657-b3f0-47fc-a4cf-281413e4765d
​     watchDurationMillis = 5652
​     retainedDurationMillis = 650

METADATA

Build.VERSION.SDK_INT: 29
Build.MANUFACTURER: samsung
LeakCanary version: 2.4
App process name: com.example.app
Analysis duration: 18616

I had the same issue. The simplest solution I found is to equate everything to Null.

override fun onDestroy() {
        super.onDestroy()
        adLoadCallback = null
        mRewardedAd = null
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM