This is a real quick post to give you some pointers of how to use Google AdMob banner ads in an Android Jetpack Compose view layout.
Right off the bat I want to say two things:
1- The AdMob team hasn’t got their library up to date (in terms of supporting compose), so it’s always going to be a bit of a hack to use AdMob with compose
2- The below feels like a hack, because we load the advert from within the compose hierarchy, it works! But it’s not the “ideal” solution. However whilst we have this interoperability, it may be the best way.
Here are the offical docs for implementing an AdMob banner advert in an Android app, the examples show you how to do it programmatically or using an XML layout (remember those? :-)). Quick Start then Implement a Banner Ad.
This is where the compose ‘hack’ differs (and you can probably improve on this), so let’s start here. The docs say to load your Ad in onCreate:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) MobileAds.initialize(this) {} mAdView = findViewById(R.id.adView) val adRequest = AdRequest.Builder().build() mAdView.loadAd(adRequest) }
You could also do this with compose, after composition calling loadAd from onCreate. But let’s do it another way:
import androidx.compose.foundation.background import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView import com.blundell.tut.R import com.google.android.gms.ads.AdRequest import com.google.android.gms.ads.AdSize import com.google.android.gms.ads.AdView @Composable fun AdvertView(modifier: Modifier = Modifier) { val isInEditMode = LocalInspectionMode.current if (isInEditMode) { Text( modifier = modifier .fillMaxWidth() .background(Color.Red) .padding(horizontal = 2.dp, vertical = 6.dp), textAlign = TextAlign.Center, color = Color.White, text = "Advert Here", ) } else { AndroidView( modifier = modifier.fillMaxWidth(), factory = { context -> AdView(context).apply { adSize = AdSize.BANNER adUnitId = context.getString(R.string.ad_id_banner) loadAd(AdRequest.Builder().build()) } } ) } } @Preview(showBackground = true) @Composable fun AdvertPreview() { AdvertView() }
Create a new composable, use the AdMob supplied AdView
wrap that in a compose supplied AndroidView
and wrap both of those in your own created composable AdvertView
.
The compose preview does not like rendering AdView when used like this, and so we validate the AdMob AdView
for use in the equivalent of isInEditMode
for compose, which is called LocalInspectionMode.current
. This allows our preview to render a red banner, instead of the advert in a preview.
The official documentation for AndroidView
explains this:
Composes an Android layout resource in the presence of
https://developer.android.com/reference/kotlin/androidx/compose/ui/viewinterop/package-summary#AndroidView(kotlin.Function1,androidx.compose.ui.Modifier,kotlin.Function1)ViewBinding
. The binding is obtained from thefactory
block, which will be called exactly once to obtain theViewBinding
to be composed, and it is also guaranteed to be invoked on the UI thread. Therefore, in addition to creating theViewBinding
, the block can also be used to perform one-off initializations andView
constant properties’ setting. Theupdate
block can be run multiple times (on the UI thread as well) due to recomposition, and it is the right place to setView
properties depending on state. When state changes, the block will be reexecuted to set the new properties. Note the block will also be ran once right after thefactory
block completes.
This explanation seems to suggest we could use either of the two properties: factory
(which we do) or update
to load our advert. They both run on the UI thread (same as running loadAd
from onCreate
.) As a judgement for the reader, you have think if you want the loadAd
method to be called on each composition or as a one off when the view is instantiated.
And that’s it! Bear in mind, calling loadAd
in this way is not recommended by anyone, but since it’s using the factory
of AndroidView
it feels ok 🙂 (If you used update
, I bet the internal library handles us calling it multiple times anyway). This example is just to get you started with AdMob and compose.
If anyone wants a more full featured example, please reach out to me on Twitter and I can make an example project.
Enjoy!
If you like this post, perhaps you will like: Taming your gradle dependencies: Just BECAUSE!
Thanks a lot for this! This is the only example on the internet about how to show AdMob ads in Jetpack Compose.