Search code examples
androidkotlinadmob

How do I get Android Admob App Open Ads to show in 21.4.0?


I just upgraded from Admob 20.6.0 to 21.4.0 in an Android project. Up until the upgrade, Admob App Open ads were working as expected. In 21.4.0 I am getting an error in showAdIfAvailable()

Type mismatch. Required: Activity Found: Activity?

On this line:

appOpenAd?.show(currentActivity)

Code correct suggests the following, but the problem is this code never runs.

currentActivity?.let { appOpenAd?.show(it) }

Here's the full code for AppOpenManager:

class AppOpenManager(private val myApplication: MainActivity) : DefaultLifecycleObserver, Application.ActivityLifecycleCallbacks {
    private var appOpenAd: AppOpenAd? = null
    private var loadCallback: AppOpenAdLoadCallback? = null
    private var currentActivity : Activity? = null
    private var isShowingAd : Boolean = false
    private var loadTime:Long = 0

    /** Creates and returns ad request.  */
    private fun getAdRequest():AdRequest {
        return AdRequest.Builder().build()
    }
    /** Utility method that checks if ad exists and can be shown. */
    private fun isAdAvailable():Boolean {
        return appOpenAd != null && wasLoadTimeLessThan4HoursAgo()
    }



    /** Constructor  */
    init {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            this.myApplication.registerActivityLifecycleCallbacks(this)
        }
        ProcessLifecycleOwner.get().lifecycle.addObserver(this)
    }


    /** Request an ad  */
    fun fetchAd() {

        if (isAdAvailable()) {
            // Have unused ad, no need to fetch another.
            return
        }
        loadCallback = object : AppOpenAdLoadCallback() {

            override fun onAdLoaded(ad: AppOpenAd) {
                this@AppOpenManager.appOpenAd = ad
                this@AppOpenManager.loadTime = (Date()).time
            }

            override fun onAdFailedToLoad(loadAdError: LoadAdError) {
                println("onAppOpenAdFailedToLoad $loadAdError")
            }

        }
        val request: AdRequest = getAdRequest()

        AppOpenAd.load(myApplication, StaticData.appOpenID, request, AppOpenAd.APP_OPEN_AD_ORIENTATION_PORTRAIT, loadCallback as AppOpenAdLoadCallback)

    }


    override fun onStart(owner: LifecycleOwner) {
        super.onStart(owner)
        showAdIfAvailable()
        println("onStart")
    }


    private fun showAdIfAvailable() {
        // Only show ad if there is not already an app open ad currently showing and an ad is available.
        if (!isShowingAd && isAdAvailable()){
            println("Will show ad.")
            val fullScreenContentCallback = object: FullScreenContentCallback() {
                override fun onAdDismissedFullScreenContent() {
                    // Set the reference to null so isAdAvailable() returns false.
                    this@AppOpenManager.appOpenAd = null
                    isShowingAd = false
                    fetchAd()
                }
                override fun onAdFailedToShowFullScreenContent(adError: AdError) {}
                override fun onAdShowedFullScreenContent() {
                    isShowingAd = true
                }
            }
            appOpenAd?.fullScreenContentCallback = fullScreenContentCallback
            appOpenAd?.show(currentActivity) // Type mismatch. Required: Activity Found: Activity?
        }
        else
        {
            println("Can not show ad... FETCH ONE INSTEAD!")
            fetchAd()
        }
    }


    /** Utility method to check if ad was loaded more than n hours ago. */
    private fun wasLoadTimeLessThan4HoursAgo():Boolean {
        val dateDifference = (Date()).time - this.loadTime
        val numMilliSecondsPerHour:Long = 3600000
        return (dateDifference < (numMilliSecondsPerHour * 4))
    }

    override fun onActivityPaused(activity: Activity) {
        println("⚠️ onActivityPaused")
    }

    override fun onActivityStarted(activity: Activity) {
        println("⚠️ onActivityStarted")
        currentActivity = activity
    }

    override fun onActivityDestroyed(activity: Activity) {
        println("⚠️ onActivityDestroyed")
        currentActivity = null
    }

    override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
        println("⚠️ onActivitySaveInstanceState")
    }

    override fun onActivityStopped(activity: Activity) {
        println("⚠️ onActivityStopped")
    }

    override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
        println("⚠️ onActivityCreated")
    }

    override fun onActivityResumed(activity: Activity) {
        println("⚠️ onActivityResumed")
        currentActivity = activity
    }

}

And in MainActivity:

private var appOpenManager: AppOpenManager? = null

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    //...
    appOpenManager = AppOpenManager(this)
}

How do I get Android Admob App Open Ads to show in 21.4.0


Solution

  • Type mismatch. Required: Activity Found: Activity?

    this error came because they made a major change

    Added @NonNull annotations in every method that previously did not explicitly define nullability. -- in 21.0.0


    but the problem is this code never runs currentActivity?.let { appOpenAd?.show(it) }

    That is a correct way to solve the above error. I'll assume here that currentActivity is null or addOpenAd is null so the show(it) function call doesn't happen. Without logcat it's hard to see the root cause is, but here are a few things to check. I think one of these will be the solution:

    1. Ensure that onAdFailedToLoad doesn't get called by looking for the log line. This will make sure that appOpenAd is assigned.
    2. Make sure you're testing on Q+, because only on those versions will your code work. On older versions currentActivity will never get assigned. Because the lifecycle callbacks from registerActivityLifecycleCallbacks will not be registered.
    3. It could be a timing issue too, you might need an additional code path to prepare for this case:
      • App starts and ad starts loading in the background
        (appOpenAd = null, currentActivity = null)
      • Activity is started and shown
        (appOpenAd = null, currentActivity = !null)
        showAdIfAvailable is called, but there's no ad yet
        BUG: it'll start loading another one!
      • Ad finishes loading
        (appOpenAd = !null, currentActivity = !null)
        BUG: nothing else is called from onAdLoaded