androidkotlinandroid-recyclerviewadmobinterstitial

InterstitialAd returns null even after loaded


I have loaded the interstitial Ad at the beginning of the Activity, but when I am showing it, it is returning a null value. Log.

2022-11-18 09:53:45.102 18604-18604/com.blogspot.bunnylists.maate D/ADAD: Ad was loaded.
2022-11-18 09:54:17.358 18604-18604/com.blogspot.bunnylists.maate D/ADAD: The interstitial wasn't loaded yet.

The app is a single-activity app. I have followed the official documentation to implement an interstitial Ad. All ad-related code is in the MainActivity. After clicking on a button, I am showing a Fullscreen fragment on the MainActivityLayout. I have implemented a ReyclerView in that fragment. When recyclerView loaded some number of views on the screen, then I want to show the Ad. I am calling the showAd() method of MainActivity class inside the onBindViewHolder method.

class MainActivity : AppCompatActivity() {
    private lateinit var bind : ActivityMainBinding
    private lateinit var viewModel: MainActivityViewModel
    private var mInterstitialAd: InterstitialAd? = null
    val TAG = "ADAD"
    val adRequest = AdRequest.Builder().build()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        bind = DataBindingUtil.setContentView(this, com.blogspot.bunnylists.maate.R.layout.activity_main)
        val repository = (application as MyApplication).repository
        viewModel = ViewModelProvider(this, MyViewModelFactory(repository, "MainActivity"))[MainActivity2ViewModel::class.java]

        //Here I am loading the Ad at the creation of the MainActivity
        loadAd()

        mInterstitialAd?.fullScreenContentCallback = object: FullScreenContentCallback() {
            override fun onAdClicked() {
                // Called when a click is recorded for an ad.
                Log.d(TAG, "Ad was clicked.")
            }

            override fun onAdDismissedFullScreenContent() {
                // Called when ad is dismissed.
                Log.d(TAG, "Ad dismissed fullscreen content.")
                mInterstitialAd = null
            }

            override fun onAdFailedToShowFullScreenContent(p0: AdError) {
                // Called when ad fails to show.
                Log.e(TAG, "Ad failed to show fullscreen content.")
                mInterstitialAd = null
            }

            override fun onAdImpression() {
                // Called when an impression is recorded for an ad.
                Log.d(TAG, "Ad recorded an impression.")
            }

            override fun onAdShowedFullScreenContent() {
                // Called when ad is shown.
                Log.d(TAG, "Ad showed fullscreen content.")
            }
        }

    }

   // The method which is called from the onBindViewHolder
    fun showAd() {
        if (mInterstitialAd != null) {
            Log.d(TAG, "showing Ad")
            mInterstitialAd?.show(this)
            loadAd()
        } else {
            Log.d(TAG, "The interstitial wasn't loaded yet.")
        }
    }

    private fun loadAd(){
        InterstitialAd.load(this,
            "ca-app-pub-3940256099942544/1033173712",
            adRequest,
            object : InterstitialAdLoadCallback() {
                override fun onAdFailedToLoad(adError: LoadAdError) {
                    Log.d(TAG, adError.toString())
                    mInterstitialAd = null
                }

                override fun onAdLoaded(interstitialAd: InterstitialAd) {
                    Log.d(TAG, "Ad was loaded.")
                    mInterstitialAd = interstitialAd
                }
            })
    }
}

onBindViewHolder:

override fun onBindViewHolder(holder: MyViewHolder, position: Int){
    if(position%20==0){
       MainActivity().showAd()
    }
}

Solution

  • Never do this:

    MainActivity()
    

    The above is instantiating an Activity by calling its constructor. This has two problems:

    1. It's not the same instance you were working with so it has not been set up with your ads.
    2. If it's not created by the OS, then it is not set up correctly. It's full of null objects that shouldn't be null so it is very prone to causing crashes. It's also just nonsensical because you're not using it as an Activity--there's no way to get an Activity instance you created yourself onto the screen.

    You need to expose an ad-showing callback from your adapter and call that instead:

    class MyAdapter: RecyclerView.Adapter<SomeType, SomeViewHolder>() {
    
        var onRequestAd: (()->Unit)? = null
    
        override fun onBindViewHolder(holder: MyViewHolder, position: Int){
            if(position%20==0){
               onRequestAd?.invoke()
            }
            //...
        }
    }
    

    Then in your Activity, right after you create your adapter instance, you should set the listener to call showAd():

    val adapter = MyAdapter()
    adapter.onRequestAd = ::showAd // or { showAd() }
    bind.adapter = adapter
    

    Separate problem I mentioned in your comments. In this line:

    mInterstitialAd?.fullScreenContentCallback = object: FullScreenContentCallback() {
    

    you are calling at a moment when mInterstitialAd is still null, so it's not doing anything and your callback is never used. You need to move this code inside the onAdLoaded callback function so it is added to the ad after it is created, and so it's added every time you load a new ad.


    By the way, you might want a more sophistocated way of determining when to show ads. As it is, I think if the 20th item is just off screen and the user jiggles it up and down, it will keep requesting ads over and over. You might want to add a timer, for example:

    private var lastAdTime = 0L
    
    override fun onBindViewHolder(holder: MyViewHolder, position: Int){
        if(position%20==0 && lastAdTime + 3000 < System.currentTimeMillis()){
           onRequestAd()
           lastAdTime = System.currentTimeMillis()
        }
    }