はじめに
アプリの収益化手段として、AdMob などのネットワーク広告を表示する方法があります。また、2024年現在 Android アプリの UI 構築には多くのアプリで Jetpack Compose が採用されています。しかし AdMob をはじめとする多くのネットワーク広告 SDK は View でのみ提供されています。そのため、Jetpack Compose で構築された UI に AdMob の広告を表示するには、相互運用 API を活用する必要があります。
Jetpack Compose に AdMob を表示する
Jetpack Compose に View を表示する方法は公式ドキュメントの Compose でビューを使用するに記載されています。この手順に従い AndroidView Composesable 関数を使用して AdMob を表示します。
AndroidView(
modifier = Modifier
.fillMaxWidth()
.height(50.dp),
factory = { context ->
val adRequest = AdRequest.Builder().build()
val adView = AdView(context)
adView.adUnitId = "ca-app-pub-3940256099942544/9214589741"
adView.setAdSize(AdSize.BANNER)
adView.loadAd(adRequest)
adView
},
)
※ adUnitId はテスト用
LazyColumn に AdMob を表示する
LazyColumn の要素として AdMob を表示する場合、注意点がひとつあります。上記の Composable 関数を LazyColumn の要素として使用すると、広告は一応表示されますが、スクロールアウトして再度スクロールインするたびに AdView インスタンスが生成され、コンテンツの再読み込みが行われてしまいます。このため、広告が真っ白な状態で表示される時間が長くなり、クリックされにくくなる可能性があります。
そこで公式ドキュメントの Compose でビューを使用するに戻ると、遅延リストの AndroidView という節がありました。こちらにはこのように書かれていました。和訳が機械翻訳で分かりにくかったため、原文を自分で意訳しました。
This overload allows Compose to reuse the underlying View instance when the containing composition is reused as is the case for Lazy lists.
Lazy List のようにコンポジションが再利用されるとき、View インスタンスは再利用されます。
onReset - A callback invoked to signal that the View is about to be reused. This must be non-null to enable View reuse.
onReset - View が再利用される時に呼ばれます。View の再利用を有効にするときは、non-null にする必要があります。
よって、onReset メソッドに対して non-null を設定します。
AndroidView(
modifier = Modifier
.fillMaxWidth()
.height(50.dp),
factory = { context ->
val adRequest = AdRequest.Builder().build()
val adView = AdView(context)
adView.adUnitId = "ca-app-pub-3940256099942544/9214589741"
adView.setAdSize(AdSize.BANNER)
adView.loadAd(adRequest)
adView
},
// 追加
onReset = { },
)
その結果、AdView も再利用されるため、スクロールアウトして再度スクロールインしても、最初から広告が表示されるようになりました。
バージョン 1.4.0-rc01 より前の Compose では、View の再利用を自作する必要があった
さて Lazy List による View インスタンスの再利用は 遅延リストの AndroidView に記載されているように、バージョン 1.4.0-rc01 (2023年3月8日)で導入されたものです。この節では、それより前はどのように LazyColumn に AdMob を表示していたのかを紹介します。
独自に View インスタンス再利用の仕組みを構築していました。
まずは View インスタンスを Queue で管理するクラスを作成します。
abstract class ViewRecycler<V : View, P> {
private val viewQueue = mutableListOf<V>()
/**
* View インスタンスを作るか、キューから取り出す
*/
fun createOrDequeue(context: Context, param: P): V {
return if (viewQueue.isEmpty()) {
// キューが空の時は View を作って返却する
createView(context, param)
} else {
// キューから取り出して返却する
viewQueue.removeAt(0)
}
}
/**
* View インスタンスを作成する
*/
abstract fun createView(context: Context, param: P): V
/**
* 使用が終わった View をキューに追加する
*/
fun enqueue(view: V) {
viewQueue.add(view)
}
}
こちらは、様々な View に対応した abstract なクラスなので、継承して AdView クラスに対応したクラスを作成します。
class AdViewRecycler : ViewRecycler<AdView, Unit>() {
override fun createView(context: Context, param: Unit): AdView {
val adRequest = AdRequest.Builder().build()
val adView = AdView(context)
adView.adUnitId = "ca-app-pub-3940256099942544/9214589741"
adView.setAdSize(AdSize.BANNER)
adView.loadAd(adRequest)
adView
}
}
AdViewRecycler クラスのインスタンスは画面 の Composable 関数など、LazyColumn が表示されている間は生存するように設置します。
val adViewRecycler = remember {
AdViewRecycler()
}
それを使い LazyColumn の要素にする AndroidView Composable 関数は、このように使います。
@Composable
fun RecyclableAdMobBannerAndroidView(adViewRecycler: AdViewRecycler) {
AndroidView(
modifier = Modifier
.fillMaxWidth()
.height(50.dp),
factory = { context ->
val frameLayout = FrameLayout(context)
frameLayout.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
override fun onViewAttachedToWindow(view: View) {
// FrameLayout が Attach されたら、AdView を FrameLayout に追加する
if (frameLayout.childCount == 0) {
// AdView は作成するか Queue から取り出す
val adView = adViewRecycler.createOrDequeue(context, Unit)
frameLayout.addView(adView)
}
}
override fun onViewDetachedFromWindow(view: View) {
// FrameLayout が Detach されたら、AdView を FrameLayout から取り除いて Queue に戻す
if (frameLayout.childCount >= 1) {
val adView = frameLayout.getChildAt(0) as AdView
// 親 GroupView から取り除かないと、次の追加でエラーになる
frameLayout.removeView(adView)
adViewRecycler.enqueue(adView)
}
}
})
frameLayout
}
)
}
まとめ
- LazyColumn に AdMob を表示するときは、再利用の仕組みが有効になっているかに注意する必要があります。
- 再利用の仕組みは Compose 1.4.0-rc1 から標準搭載されています。
- リリース日である2023年3月8日から1年半が経過しており、遅れた感のある記事ですが、Compose の進化を感じていただけると幸いです。