●TOOLING — Rork's developer repos keep moving: rork-xcode was updated on July 16, rork-device on July 15, and rork-plist on July 13●OPUS46 — Claude Opus 4.6 is live in Rork, and Rork Max is built to assemble apps on top of Claude Code●SIM — A cloud iOS simulator runs in the browser, with one click to install on a device and two clicks to publish to the App Store●MAX — Rork Max emits pure Swift rather than React Native, reaching iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and even iMessage●NATIVE — That opens up HealthKit, ARKit and LiDAR, NFC, Dynamic Island, Live Activities, 3D through Metal, and on-device inference with Core ML●SEED — Rork raised a $15M seed led by Left Lane Capital, with Peak XV and a16z Speedrun joining the round●TOOLING — Rork's developer repos keep moving: rork-xcode was updated on July 16, rork-device on July 15, and rork-plist on July 13●OPUS46 — Claude Opus 4.6 is live in Rork, and Rork Max is built to assemble apps on top of Claude Code●SIM — A cloud iOS simulator runs in the browser, with one click to install on a device and two clicks to publish to the App Store●MAX — Rork Max emits pure Swift rather than React Native, reaching iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and even iMessage●NATIVE — That opens up HealthKit, ARKit and LiDAR, NFC, Dynamic Island, Live Activities, 3D through Metal, and on-device inference with Core ML●SEED — Rork raised a $15M seed led by Left Lane Capital, with Peak XV and a16z Speedrun joining the round
Taking Inventory of Your AdMob Ad Units — All the Way to App Open Cooldown Design
When rewarded and interstitial units pile up and you can no longer tell which one fires on which screen, here is the inventory process I use, plus the design for adding a cooldown to an App Open ad that otherwise shows on every launch.
Run a wallpaper app with 50 million cumulative downloads long enough and the ad units quietly multiply. It started with a single interstitial; then I added a second for A/B testing, dropped in a rewarded unit, tried an App Open ad, and so on. One day I realized I could no longer instantly answer "which screen does this unit ID show on?" For something tied directly to revenue, that vagueness is a risky place to operate from.
So before adding anything new, I decided to take a proper inventory. What I did is not flashy, but I think it helps other solo developers whose ad setup has grown the same way, so I am leaving the steps here.
Start by listing every unit with grep
AdMob ad unit IDs are embedded as strings in code, so the definitions are easy to find. On Android, one line is enough.
This surfaces the files where I define the constants (in my case Globals.java and AdManager.java) along with anything left over in layout XML. Reading the output, I use the last few digits of each unit ID as a key and map out where each one is defined and where it is called from.
The biggest win from this pass was finding the remains of an old banner unit still sitting in XML. Nothing in code referenced it anymore, yet the layout kept the AdView definition alive. You never notice these "ghost units" without an inventory.
✦
Thank you for reading this far.
Continue Reading
What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.
WHAT YOU'LL LEARN
✦A concrete process for matching ad unit definitions to call sites using nothing but grep
✦How I add a four-hour cooldown and a first-launch skip to an App Open ad
✦The judgment I use so frequency capping and review prompts do not collide
Secure payment via Stripe · Cancel anytime
✦
Unlock This Article
Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.
I keep the results in a table, not in my head. I organized it with these columns:
Unit name (the name in the AdMob console)
Last few digits of the unit ID
Type (interstitial / rewarded / App Open / banner)
Screen used (which Activity or Fragment shows it)
Frequency control (which setting governs how it appears)
That last column matters most. In my app, interstitial frequency is controlled from the server with action_inters_frequency (how many actions between shows) and action_inters_max (a per-session cap). The rewarded unit uses a combination of reward_period, reward_launch_count, and reward_action_count, looking at both launch count and action count to decide whether to show. Put into a table, it became obvious at a glance that this control logic varied unit by unit.
An App Open ad must not fire on every launch
The thing I most wanted to fix during the inventory was the App Open ad I had bolted on later. Checking the implementation, it showed unconditionally on every cold start. That reliably hurts the experience. Wedge a full-screen ad in front of every open and the users most focused on just launching feel the most friction. AdMob policy also expects a design that lets users pass straight through when the ad has not finished loading.
So I moved to showing it only when three conditions hold:
Skip it on the first launch (the very first open after install)
A set interval has passed since the last show (I set four hours)
The ad has finished loading; if not, show the screen without waiting
The cooldown check is simple: store the last shown time and compare the delta.
class AppOpenManager(private val prefs: SharedPreferences) { private val cooldownMillis = 4 * 60 * 60 * 1000L // 4 hours fun canShow(): Boolean { val isFirstLaunch = prefs.getBoolean("is_first_launch", true) if (isFirstLaunch) { prefs.edit().putBoolean("is_first_launch", false).apply() return false // skip the first launch } val last = prefs.getLong("app_open_last_shown", 0L) return System.currentTimeMillis() - last >= cooldownMillis } fun markShown() { prefs.edit().putLong("app_open_last_shown", System.currentTimeMillis()).apply() }}
On the display side, if canShow() is false I do not even fetch the ad, and even when it is true, I pass through without waiting if the load has not completed.
fun showIfAvailable(activity: Activity, onDone: () -> Unit) { if (!manager.canShow() || appOpenAd == null) { onDone() // proceed to the screen without showing an ad return } appOpenAd?.fullScreenContentCallback = object : FullScreenContentCallback() { override fun onAdDismissedFullScreenContent() { manager.markShown() appOpenAd = null onDone() } } appOpenAd?.show(activity)}
The trick is making sure onDone() always runs. Whether or not an ad shows, you guarantee the user ultimately reaches the screen they wanted. Forget this and a failed ad load can stall navigation — the worst kind of bug.
Do not stack ads and review prompts
One more thing the inventory revealed: ad display and the in-app review prompt (governed by review_action_count) could collide in timing. If the review dialog appears right after a full-screen ad closes, it reads to the user as one continuous interruption — "nagged right after an ad" — and tends to drag down ratings. So I separated the counters, triggering the review request on a different action than the one that shows an ad.
Ads are a revenue pillar, but show too many and you erode retention, which ultimately thins out long-term revenue. I always weigh "how much does this one ad lower the odds they open the app again tomorrow?" when setting frequency.
Inventory comes before any revenue play
Before trying a new ad format, accurately grasp what you already have. It is unglamorous, but once you know "which lever moves what," subsequent revenue work gets far easier. For me, this inventory made the next move — the App Open cooldown — surface naturally.
If your ad setup feels like it has grown unwieldy, start with grep "ca-app-pub" before adding a feature. It is astonishing how much it clears your head.
Share
Thank You for Reading
Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.