Skip to content

Keep asking for a banner after one does not fill - #643

Merged
andiwand merged 1 commit into
mainfrom
keep-asking-after-a-banner-does-not-fill
Sep 1, 2026
Merged

Keep asking for a banner after one does not fill#643
andiwand merged 1 commit into
mainfrom
keep-asking-after-a-banner-does-not-fill

Conversation

@andiwand

@andiwand andiwand commented Sep 1, 2026

Copy link
Copy Markdown
Member

showAdaptiveBanner() called loadAd() on an AdView that was still detached from the view hierarchy and added it to the container only inside onAdLoaded. On onAdFailedToLoad the house ad took the container instead, so the AdView never entered the hierarchy at all.

The ad unit's automatic refresh only runs for a banner that is on screen, and nothing else asks again inside a session: showGoogleAds() runs once per launch and refreshAds() only on a configuration change. One request that did not fill therefore ended the session's advertising until the user rotated the screen or came back to the app.

What changes

One banner for the life of the activity. The AdView is built once and kept — no destroy on a configuration change, so nothing tears out a banner mid-request, the slot never collapses while a request is in flight, and there can be no second AdView to be stale against. A width that has not changed asks for nothing, which is what AdSlot.swift on iOS already does; a keyboard opening or a rotation back is now free.

Both views take turns. The banner and the house ad are in the container together, exactly one visible at a time. The house ad is never stacked over a live banner — an impression nobody can see is invalid traffic.

A failure asks again, rescheduling itself before each request so one that never comes back cannot end the chain and leave the slot as silent as before. The delay doubles up to the rate the unit refreshes at, so a slot that is never going to fill settles on what the sdk would have done anyway. The first ask is not shorter than 10s on purpose: the sdk throttles a burst of failed requests by itself, and an ask it answers never reaches the auction.

The lifecycle drives it. onPause/onResume stop and restart both the retry and the sdk's own refresh, replacing a hasWindowFocus() check that never backed off while unfocused and stalled on any dialog that took focus — including the consent form.

A refresh that does not fill keeps the ad already on screen instead of hiding a live one behind the house ad, which also let the sdk keep refreshing it.

Paying for ad removal destroys the banner. removeAds() previously left it attached and refreshing for someone who had just bought their way out of it, and a late callback could still advance the house ad's stored rotation and report a house ad that was never displayed.

The house-ad rotation moves when one is shown, not when one is built, so a session that never fills no longer walks through all three texts.

Notes

  • The premise is that an attached but GONE AdView accepts loadAd() and delivers onAdLoaded — true for the sdk version pinned here, and the explicit retry means the fix does not depend on the sdk refreshing a hidden banner.
  • Ads are disabled under instrumentation (IS_TESTING), so no automated test covers this path. Worth a manual pass on a device forced to no-fill.
  • Left alone, and the same on both platforms: a first launch with no network leaves consent undecided and nothing asks again in that session. That deserves its own change.
  • iOS has the same hole and gets the same retry in Keep asking for a banner after one does not fill OpenDocument.ios#188.

🤖 Generated with Claude Code

The AdView was loaded while still detached and only added to the container in
onAdLoaded; on a failure the house ad took the container instead, so it never
entered the hierarchy at all. The ad unit only refreshes a banner that is on
screen, and nothing else asks again inside a session - showGoogleAds runs once
per launch and refreshAds only on a configuration change - so one request that
did not fill ended the session's advertising.

The banner is now built once and kept for the life of the activity, beside the
house ad, exactly one of them shown at a time. A configuration change lays the
house ad out again and asks for a new banner only when the width really changed,
as AdSlot does on iOS; nothing is torn out mid-request and the slot never
collapses while one is in flight.

A failure schedules its own ask, rescheduling before each request so one that
never answers cannot end the chain, and onPause/onResume stop and start both it
and the sdk's refresh. A refresh that does not fill keeps the ad already on
screen, and paying for ad removal destroys the banner rather than leaving it
attached and refreshing.
@andiwand
andiwand force-pushed the keep-asking-after-a-banner-does-not-fill branch from 7113997 to 31f91dd Compare September 1, 2026 19:25
@andiwand
andiwand merged commit 8f70a08 into main Sep 1, 2026
7 checks passed
@andiwand
andiwand deleted the keep-asking-after-a-banner-does-not-fill branch September 1, 2026 19:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant