I am trying to implement InterstitialAd in Jetpack Compose. This is my AdsHelpder.kt class:
class AdsHelper(private val context: Context) : FullScreenContentCallback() {
private var onAdDismissed: (() -> Unit)? = null
private var interstitialAd: InterstitialAd? = null
private val adRequest = AdRequest.Builder().build()
fun onDestroy() {
interstitialAd?.fullScreenContentCallback = null
interstitialAd = null
}
fun onLoadInterstitialAd(canLoad: Boolean) {
val id = context.onString(R.string.interstitial_ad_id)
if (canLoad) {
InterstitialAd.load(context, id, adRequest, object : InterstitialAdLoadCallback() {
override fun onAdLoaded(p0: InterstitialAd) {
interstitialAd = p0
}
override fun onAdFailedToLoad(p0: LoadAdError) {
interstitialAd = null
}
})
}
}
override fun onAdDismissedFullScreenContent() {
interstitialAd = null
onAdDismissed?.invoke()
}
fun onShowInterstitialAd(onAdDismissed: () -> Unit) {
interstitialAd?.apply {
show(context.onMainActivity())
fullScreenContentCallback = this@AdsHelper
}
this.onAdDismissed = onAdDismissed
}
override fun onAdFailedToShowFullScreenContent(e: AdError) {
interstitialAd = null
}
}
Since I need to show the InterstitialAd in several screens, I think this implementation way looks optimal for MainActivity.kt:
class MainActivity : ComponentActivity() {
private lateinit var data: Data
private var adsHelper: AdsHelper? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
data = ViewModelProvider(this)[Data::class.java]
adsHelper = AdsHelper(this)
setContent {
AppTheme {
val controller = rememberAnimatedNavController()
controller.addOnDestinationChangedListener { _, route, _ ->
adsHelper?.onLoadInterstitialAd(route != HOME)
}
LaunchedEffect(data.interstitial) {
when {
data.interstitial -> adsHelper?.onShowInterstitialAd {
data.interstitial = false
}
}
}
Column {
Scaffold(
Modifier.weight(1f)
) {
NavGraph(controller, data)
}
}
}
}
}
override fun onDestroy() {
adsHelper?.onDestroy()
super.onDestroy()
}
}
Here the Data.kt:
class Data(app: Application) : AndroidViewModel(app) {
var interstitial by mutableStateOf(false)
}
I could avoide these lines in the MainActivity.kt
...
controller.addOnDestinationChangedListener { _, route, _ ->
adsHelper?.onLoadInterstitialAd(route != HOME)
}
...
and directly call adsHelper?.onLoadInterstitialAd(true), but it causes endless logs:
...
D/CCodecConfig: c2 config diff is c2::u32 default.color.matrix = 1
c2::u32 default.color.primaries = 1
c2::u32 default.color.range = 2
c2::u32 default.color.transfer = 3
c2::u32 raw.max-size.height = 360
c2::u32 raw.max-size.width = 640
c2::u32 raw.size.height = 360
c2::u32 raw.size.width = 640
W/Codec2Client: query -- param skipped: index = 1107298332.
D/CCodec: client requested max input size 86400, which is smaller than what component recommended (6291456); overriding with component recommendation.
W/CCodec: This behavior is subject to change. It is recommended that app developers double check whether the requested max input size is in reasonable range.
D/CCodec: encoding statistics level = 0
D/CCodec: setup formats input: AMessage(what = 0x00000000) = {
int32_t height = 360
int32_t level = 256
int32_t max-input-size = 6291456
string mime = "video/x-vnd.on2.vp9"
int32_t profile = 1
int32_t width = 640
Rect crop(0, 0, 639, 359)
}
D/CCodec: setup formats output: AMessage(what = 0x00000000) = {
int32_t android._color-format = 2135033992
int32_t android._video-scaling = 1
Rect crop(0, 0, 639, 359)
int32_t color-standard = 1
int32_t color-range = 2
int32_t color-transfer = 3
Buffer hdr10-plus-info = {
}
int32_t height = 360
int32_t max-height = 360
int32_t max-width = 640
string mime = "video/raw"
int32_t rotation-degrees = 0
int32_t sar-height = 1
int32_t sar-width = 1
int32_t width = 640
int32_t android._dataspace = 260
int32_t color-format = 2130708361
}
I/CCodecConfig: query failed after returning 11 values (BAD_INDEX)
W/Codec2Client: query -- param skipped: index = 1107298332.
D/CCodec: client requested max input size 86400, which is smaller than what component recommended (6291456); overriding with component recommendation.
W/CCodec: This behavior is subject to change. It is recommended that app developers double check whether the requested max input size is in reasonable range.
D/CCodec: encoding statistics level = 0
D/CCodec: setup formats input: AMessage(what = 0x00000000) = {
int32_t height = 360
int32_t level = 256
int32_t max-input-size = 6291456
string mime = "video/x-vnd.on2.vp9"
int32_t profile = 1
int32_t width = 640
Rect crop(0, 0, 639, 359)
}
D/CCodec: setup formats output: AMessage(what = 0x00000000) = {
int32_t android._color-format = 2135033992
int32_t android._video-scaling = 1
Rect crop(0, 0, 639, 359)
int32_t color-standard = 1
int32_t color-range = 2
int32_t color-transfer = 3
Buffer hdr10-plus-info = {
}
int32_t height = 360
int32_t max-height = 360
int32_t max-width = 640
string mime = "video/raw"
int32_t rotation-degrees = 0
int32_t sar-height = 1
int32_t sar-width = 1
int32_t width = 640
int32_t android._dataspace = 260
int32_t color-format = 2130708361
}
I/CCodecConfig: query failed after returning 11 values (BAD_INDEX)
D/MediaCodec: keep callback message for reclaim
D/MediaCodec: keep callback message for reclaim
W/Codec2Client: query -- param skipped: index = 1342179345.
W/Codec2Client: query -- param skipped: index = 2415921170.
W/Codec2Client: query -- param skipped: index = 1342179345.
W/Codec2Client: query -- param skipped: index = 2415921170.
W/Codec2Client: query -- param skipped: index = 2684356609.
W/Codec2Client: query -- param skipped: index = 2684356609.
D/CCodecBufferChannel: [c2.goldfish.vp9.decoder#948] Created input block pool with allocatorID 16 => poolID 60 - OK (0)
D/CCodecBufferChannel: [c2.goldfish.vp9.decoder#415] Created input block pool with allocatorID 16 => poolID 59 - OK (0)
D/CCodecBufferChannel: [c2.goldfish.vp9.decoder#948] Query output surface allocator returned 0 params => BAD_INDEX (6)
D/CCodecBufferChannel: [c2.goldfish.vp9.decoder#415] Query output surface allocator returned 0 params => BAD_INDEX (6)
I/CCodecBufferChannel: [c2.goldfish.vp9.decoder#415] Created output block pool with allocatorID 18 => poolID 151 - OK
I/CCodecBufferChannel: [c2.goldfish.vp9.decoder#948] Created output block pool with allocatorID 18 => poolID 152 - OK
D/CCodecBufferChannel: [c2.goldfish.vp9.decoder#415] Configured output block pool ids 151 => OK
D/CCodecBufferChannel: [c2.goldfish.vp9.decoder#948] Configured output block pool ids 152 => OK
D/Codec2-OutputBufferQueue: remote graphic buffer migration 0/0
D/Codec2-OutputBufferQueue: remote graphic buffer migration 0/0
D/Codec2Client: setOutputSurface -- failed to set consumer usage (6/BAD_INDEX)
D/Codec2Client: setOutputSurface -- generation=9693208 consumer usage=0x900
D/Codec2Client: setOutputSurface -- failed to set consumer usage (6/BAD_INDEX)
D/Codec2Client: setOutputSurface -- generation=9693207 consumer usage=0x900
D/Codec2Client: Surface configure completed
D/Codec2Client: Surface configure completed
I/DynamiteModule: Considering local module com.google.android.gms.ads.dynamite:0 and remote module com.google.android.gms.ads.dynamite:230500000
I/DynamiteModule: Selected remote version of com.google.android.gms.ads.dynamite, version >= 230500000
D/CCodecConfig: c2 config diff is c2::u32 raw.channel-mask.value = 12
D/CCodecBuffers: [c2.android.aac.decoder#912:Output[N]] popFromStashAndRegister: at 1000000000000us, output format changed to AMessage(what = 0x00000000) = {
int32_t aac-drc-album-mode = 0
int32_t aac-drc-boost-level = 127
int32_t aac-drc-cut-level = 127
int32_t aac-drc-effect-type = 3
int32_t aac-drc-output-loudness = -1
int32_t aac-encoded-target-level = -1
int32_t aac-max-output-channel_count = 8
int32_t aac-target-ref-level = 64
int32_t channel-count = 2
int32_t channel-mask = 12
int32_t max-output-channel-count = 8
string mime = "audio/raw"
int32_t sample-rate = 44100
int32_t android._config-pcm-encoding = 2
}
D/CCodecConfig: c2 config diff is c2::u32 raw.crop.height = 360
c2::u32 raw.crop.left = 0
c2::u32 raw.crop.top = 0
c2::u32 raw.crop.width = 640
D/CCodecConfig: c2 config diff is c2::u32 raw.channel-mask.value = 12
D/CCodecBuffers: [c2.android.aac.decoder#806:Output[N]] popFromStashAndRegister: at 1000000000000us, output format changed to AMessage(what = 0x00000000) = {
int32_t aac-drc-album-mode = 0
int32_t aac-drc-boost-level = 127
int32_t aac-drc-cut-level = 127
int32_t aac-drc-effect-type = 3
int32_t aac-drc-output-loudness = -1
int32_t aac-encoded-target-level = -1
int32_t aac-max-output-channel_count = 8
int32_t aac-target-ref-level = 64
int32_t channel-count = 2
int32_t channel-mask = 12
int32_t max-output-channel-count = 8
string mime = "audio/raw"
int32_t sample-rate = 44100
int32_t android._config-pcm-encoding = 2
}
I/DynamiteModule: Considering local module com.google.android.gms.ads.dynamite:0 and remote module com.google.android.gms.ads.dynamite:230500000
I/DynamiteModule: Selected remote version of com.google.android.gms.ads.dynamite, version >= 230500000
V/FA: Inactivity, disconnecting from the service
D/BufferPoolAccessor2.0: evictor expired: 4, evicted: 4
I/DynamiteModule: Considering local module com.google.android.gms.ads.dynamite:0 and remote module com.google.android.gms.ads.dynamite:230500000
I/DynamiteModule: Selected remote version of com.google.android.gms.ads.dynamite, version >= 230500000
I/DynamiteModule: Considering local module com.google.android.gms.ads.dynamite:0 and remote module com.google.android.gms.ads.dynamite:230500000
I/DynamiteModule: Selected remote version of com.google.android.gms.ads.dynamite, version >= 230500000
D/CCodecConfig: c2 config diff is c2::u32 raw.crop.height = 360
c2::u32 raw.crop.left = 0
c2::u32 raw.crop.top = 0
c2::u32 raw.crop.width = 640
D/BufferPoolAccessor2.0: evictor expired: 1, evicted: 1
I/Ads: This request is sent from a test device.
...
furthermore, while generating those logs and especially showing up a InterstitialAd (when data.interstitial = true), the the app slows down/freezes.
That's why I decided to load the InterstitialAd only in target screens, in this case all screens beside the HOME. But this does not solve the problem and the logs are kept loading endless.
Moreover, according to Crashlytics and App Quality Insights there occurs a TransactionTooLargeException.
But the app does not crash, only freezes until data.interstitial = false. After that the app runs ok.
Obviously, loading and showing up the InterstitialAd consumes a lot of resources/memory, especially, if there is a video ad.
I recently started working with Jetpack Compose and many things are new to me. Therefore, my implementation could be wrong and not really optimal. What might be wrong here?

Firstly, I would recommend that you consider separating your concerns in your classes.
AdComponents.kt
class holding your
InterstitialAdandAdaptiveBannerAdimplementation.Feel free to change the above Interstitial class according to your need.
I suggest creating a separate NavGraph class to contain your composable.
AppNavGraph
MainActivity
with Hilt Dependency Injection.
MainActivity
Without Dependency Injection.