ENTOTSU44

IT系のことを中心に更新してます。

Googleのガイドが間違っている。

背景

Google AdMob のリワード広告を公式のガイドを参考にして実装しようとしたらできなかった。

問題点

2021/03/23現在、下のサイトの広告を表示するセクションのコードを実行してもonUserEarnedRewardが実行されない。

if (mRewardedAd != null){
  mRewardedAd?.show(this, OnUserEarnedRewardListener() {
    fun onUserEarnedReward(rewardItem: RewardItem) {
      var rewardAmount = rewardItem.getReward()
      var rewardType = rewardItem.getType()
      Log.d("TAG", "User earned the reward.")
    }
  })
} else {
  Log.d("TAG", "The rewarded ad wasn't ready yet.")
}

developers.google.cn

環境

解決法

基本的に変更点はshowRewardedAd()の中です。

1. 具象クラスを作ってインスタンスを入れる

package .....
.......
class MainActivity : AppCompatActivity() {
    // Admob
    private var mRewardedAd: RewardedAd? = null

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

        //Rewarded Ad
        loadRewardedAd()

        button.setOnClickListener {
            showRewardedAd()
        }
    }


    private fun loadRewardedAd() {
        val adRequest = AdRequest.Builder().build()

        RewardedAd.load(this, "ca-app-pub-3940256099942544/5224354917", adRequest, object : RewardedAdLoadCallback() {
            override fun onAdFailedToLoad(adError: LoadAdError) {
                Log.d("rewarded ads", adError.message)
                mRewardedAd = null
            }

            override fun onAdLoaded(rewardedAd: RewardedAd) {
                Log.d("rewarded ads", "Ad was loaded.")
                mRewardedAd = rewardedAd
            }
        })

        mRewardedAd?.fullScreenContentCallback = object : FullScreenContentCallback() {
            override fun onAdDismissedFullScreenContent() {
                Log.d("rewarded ads", "Ad was dismissed.")
            }

            override fun onAdFailedToShowFullScreenContent(adError: AdError?) {
                Log.d("rewarded ads", "Ad failed to show.")
            }

            override fun onAdShowedFullScreenContent() {
                Log.d("rewarded ads", "Ad showed fullscreen content.")
                // Called when ad is dismissed.
                // Don't set the ad reference to null to avoid showing the ad a second time.
                mRewardedAd = null
            }
        }
    }

    private fun showRewardedAd() {
        if (mRewardedAd != null) {
            mRewardedAd?.show(this, OnUserEarnedRewardListenerImpl())
        } else {
            Log.d("TAG", "The rewarded ad wasn't ready yet.")
        }

    }
}

class OnUserEarnedRewardListenerImpl() : OnUserEarnedRewardListener {
    override fun onUserEarnedReward(rewardItem: RewardItem) {
        var rewardAmount = rewardItem.amount
        var rewardType = rewardItem.type
        Log.d("TAG", "User earned the reward.")
    }
}

2. objectを使ってOnUserEarnedRewardListenerを継承する無名クラスのインスタンスを作る

package .....
.......
class MainActivity : AppCompatActivity() {
    // Admob
    private var mRewardedAd: RewardedAd? = null

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

        //Rewarded Ad
        loadRewardedAd()

        button.setOnClickListener {
            showRewardedAd()
        }
    }


    private fun loadRewardedAd() {
        val adRequest = AdRequest.Builder().build()

        RewardedAd.load(this, "ca-app-pub-3940256099942544/5224354917", adRequest, object : RewardedAdLoadCallback() {
            override fun onAdFailedToLoad(adError: LoadAdError) {
                Log.d("rewarded ads", adError.message)
                mRewardedAd = null
            }

            override fun onAdLoaded(rewardedAd: RewardedAd) {
                Log.d("rewarded ads", "Ad was loaded.")
                mRewardedAd = rewardedAd
            }
        })

        mRewardedAd?.fullScreenContentCallback = object : FullScreenContentCallback() {
            override fun onAdDismissedFullScreenContent() {
                Log.d("rewarded ads", "Ad was dismissed.")
            }

            override fun onAdFailedToShowFullScreenContent(adError: AdError?) {
                Log.d("rewarded ads", "Ad failed to show.")
            }

            override fun onAdShowedFullScreenContent() {
                Log.d("rewarded ads", "Ad showed fullscreen content.")
                // Called when ad is dismissed.
                // Don't set the ad reference to null to avoid showing the ad a second time.
                mRewardedAd = null
            }
        }
    }

    private fun showRewardAd() {
        if (mRewardedAd != null) {
            mRewardedAd?.show(this, object:OnUserEarnedRewardListener {
                override fun onUserEarnedReward(rewardItem: RewardItem) {
                    var rewardAmount = rewardItem.amount
                    var rewardType = rewardItem.type
                    Log.d("TAG", rewardItem.toString())
                    Log.d("TAG", "User earned the reward.")
                }
            })
        } else {
            Log.d("TAG", "The rewarded ad wasn't ready yet.")
        }
    }
}

3. ラムダ式を用いて記述する

package .....
.......
class MainActivity : AppCompatActivity() {
    // Admob
    private var mRewardedAd: RewardedAd? = null

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

        //Rewarded Ad
        loadRewardedAd()

        button.setOnClickListener {
            showRewardedAd()
        }
    }


    private fun loadRewardedAd() {
        val adRequest = AdRequest.Builder().build()

        RewardedAd.load(this, "ca-app-pub-3940256099942544/5224354917", adRequest, object : RewardedAdLoadCallback() {
            override fun onAdFailedToLoad(adError: LoadAdError) {
                Log.d("rewarded ads", adError.message)
                mRewardedAd = null
            }

            override fun onAdLoaded(rewardedAd: RewardedAd) {
                Log.d("rewarded ads", "Ad was loaded.")
                mRewardedAd = rewardedAd
            }
        })

        mRewardedAd?.fullScreenContentCallback = object : FullScreenContentCallback() {
            override fun onAdDismissedFullScreenContent() {
                Log.d("rewarded ads", "Ad was dismissed.")
            }

            override fun onAdFailedToShowFullScreenContent(adError: AdError?) {
                Log.d("rewarded ads", "Ad failed to show.")
            }

            override fun onAdShowedFullScreenContent() {
                Log.d("rewarded ads", "Ad showed fullscreen content.")
                // Called when ad is dismissed.
                // Don't set the ad reference to null to avoid showing the ad a second time.
                mRewardedAd = null
            }
        }
    }

    private fun showRewardAd() {
        if (mRewardedAd != null) {
            mRewardedAd?.show(this) { rewardItem ->
                var rewardAmount = rewardItem.amount
                var rewardType = rewardItem.type
                Log.d("TAG", rewardItem.toString())
                Log.d("TAG", "User earned the reward.")
            }
        } else {
            Log.d("TAG", "The rewarded ad wasn't ready yet.")
        }
    }
}

詳細

OnUserEarnedRewardListenerを見るとJavaで記述されたinterfaceになっている。よって、具象クラスを作り、onUserEarnedRewardメソッドをoverrideすることで任意のメソッドを作成することができる。
他にもKotlinではobjectを用いるとOnUserEarnedRewardListenerを継承する無名クラスのインスタンスを作ることができる。
また、KotlinにSAM変換があることにより、ラムダ式で記述することができる。
Kotlin 1.3系ではJavaで記述されたsingle methodしか変換できないがKotlin 1.4系ではKotlinで記述されたsingle methodも変換できるらしい。

ガイドに記述されていたJavaのコードをAndroid StudioにコピペしてKotlinへ自動変換すると同一のKotlinコードに変換された。 よって、今回のGoogleのガイドが間違っていたのは、JavaからKotlinへ自動変換し、そのコードを確認せずにガイドとして表記していることが原因として考えられる。(あくまで個人的な一説です。)