Field notes
I build and run 24 Android apps and 6 web services. These are the numbers I measured and the problems I hit along the way — console figures, real-device results, answers from Google support — nothing copied from elsewhere.
Why our lottery ticket QR scanner showed zero successes: a path you don't log looks like a failure
In our lottery app's weekly report, 19 people had opened the QR screen and GA4 showed zero successful scans. The scanner wasn't broken. The path that real ticket QR codes took had no logging at all. How we found the measurement gap, what four similar apps do, why we switched to the bundled scanning engine, and the numbers one week after the fix.
2026-10-22 · Android · GA4 · ML KitTapping the logo in the map's corner crashed the app: Naver Map SDK and the AppCompat theme
Four "You need to use a Theme.AppCompat theme" crashes showed up in Crashlytics for our lottery store map app. They came not from users but from the Play pre-launch report's test robot tapping the NAVER logo in the bottom-left corner of the map. The cause, a fix that wraps only the map instead of changing the whole app theme, and a second problem that appeared in dark mode.
2026-10-19 · Android · Naver Map SDK · Jetpack ComposeRequesting ads before the SDK finishes initializing: why the same crash came back three times after we added a guard
After we first fixed the "MobileAds.initialize must be called" crash in our live apps, the same crash came back three more times in different shapes. We had a guard that waited for initialization to finish, but a flag set at the start, a flag set on consent alone, and new ad slots that skipped the guard all left gaps. The cause of each case and the results of checking all 23 apps.
2026-10-15 · Android · AdMob · Google Mobile Ads SDKA crash from one line of image downsampling: inSampleSize starts at 0
To save memory, we changed the document thumbnails in our QR scanner app to load downsampled. In the next version, the app closed every time the document list opened. The cause was that BitmapFactory.Options.inSampleSize defaults to 0. The code we changed, the crash data, the fix, and how we checked for the same mistake elsewhere.
2026-10-13 · Android · BitmapFactory · CrashlyticsThe crashes Crashlytics didn't show: it only sees the builds you added it to
Three days after adding Firebase Crashlytics to 21 apps, we found a user-facing crash in Play Console first. Crashlytics only reports from builds that include the SDK, and crashes from older versions still on users' phones pile up only in Play vitals. Results from checking all 21 apps, and the order we check in now.
2026-10-08 · Android · Crashlytics · Play ConsoleThe day a sync deleted 8,802 stores: one renamed API field and a 100-row list
In our lottery retailer map app, a sync turned every store name into "Unknown" and wiped the 8,802 stores bundled with the app. Three causes — a renamed field in a public data API, a sync that deleted rows based on only the first 100 results, and a geocoder reading keys that do not exist — and how each was fixed.
2026-10-07 · Android · Room · Public data APIWhy users quit at the TV pairing code screen: a 6-character code and a Korean keyboard
After fixing a pairing bug in our TV remote app, the share of users who cancelled on the code entry screen went from 9% to 19%. How the GA4 failure reasons were read, how one input field turned out to open the Korean keyboard, and what was changed.
2026-10-06 · Android · EditText · KeyboarddispatchDetachedFromWindow NullPointerException: tracking an Android crash with none of my code in the stack
MdViewer hit a NullPointerException whose stack trace contained only framework frames. The cause was one line that removed a sibling view inside onDetachedFromWindow, combined with how a parent ViewGroup walks its child array. How it was reproduced, why it happens and how it was fixed.
2026-10-01 · Android · Crash · ViewHow to open .md files on Android when AI-generated markdown shows up as raw symbols
Open a .md file from ChatGPT, Claude or Codex, an Obsidian note or a GitHub README on your phone and you often see raw #, * and |. Why that happens, three ways to read markdown properly on Android, and what breaks most often, from building a markdown viewer.
2026-09-29 · Android · Markdown · ProductivityR8 on, build green, app broken: five runtime traps that actually hit across 15 apps
I enabled R8 in 15 apps for Play's 2027 code-optimization requirement. Every build passed, but 2 of the first 7 I installed broke at runtime. Gson deserialization, getIdentifier resource lookups, Room _Impl classes, and debuggable builds that silently disable R8: the kinds of failure that never show up in a crash log.
2026-09-16 · Android · R8 · Google PlayOne merchant account blocked releases for all 21 of my apps: Korean individual developers and Play Console's "additional information required"
Right after I set up a merchant account to test a donation in-app product, Play Console demanded a business registration number and an e-commerce license number and blocked every release on the account. Deleting the products did not help, deleting the payout account did not help, and support's final answer was "this cannot be reversed". How an individual developer avoids this trap.
2026-09-16 · Google Play · Play Console · policyData safety forms with zero declarations are getting caught: auditing 21 apps and fixing them with CSV and the API
A phone number the user types in by hand, belonging to someone else, still counts as "phone number collection" to Google. After one rejection I compared all 21 apps' data-safety declarations against their source: 11 were wrong, and 3 had declared nothing at all. Why ad-only apps cannot declare zero, and how to fix the form with CSV and the API instead of the five-step wizard.
2026-09-16 · Google Play · data safety · policyThe #1 app in Play search has the query in its title, verbatim: how one middle dot killed our keyword matching
I measured 157 Korean Play search queries by hand. The top-ranked app always carried the exact query string, spacing included, in its title. Our apps had been invisible for QR queries because I had joined words with a middle dot to make the title look nicer. The evidence behind retitling 13 apps.
2026-09-16 · ASO · Google Play · store optimizationA 301 cannot be taken back: how 43 minutes of a wrong redirect came back as a 404 five days later, plus the Cloudflare edge-cache incident
Moving two web services to subdomains, a broken 301 went out for 43 minutes. The server was fixed at once, but the browser was still following that 301 into a 404 five days later. What to do before emitting a permanent redirect, the only way to rescue one already sent, and the day a deploy "did nothing" because Cloudflare's edge kept the old CSS.
2026-09-16 · web · deployment · HTTPOne language per page: why I dropped IP-country detection and bilingual pages for browser language plus hreflang
Six web services each picked their initial language differently. The one using IP country served English to Googlebot only, so the search-result language could not be controlled; the landing page mixed Korean and English on one page, against Google's "one language per page" expectation. How I unified every site under one rule.
2026-09-16 · web · SEO · localizationSame app, same users, native ads paid 38x more per 1,000 impressions than banners, and why I stopped short of deleting the banner units
Seven days of AdMob data from two TV remote apps, comparing revenue per 1,000 impressions for banners and native advanced ads. How over-exposure kills the rate, how to read native's low fill rate, and why I aborted deleting the banner ad units the next day.
2026-09-16 · AdMob · ad revenue · native adsWhy switching language inside the app sometimes does nothing: setApplicationLocales only recreates activities that are on screen
"I switched to English and it stayed Korean until I restarted the app." The cause is that AppCompat recreates only the activities that are started at that moment; screens that were stopped behind it come back in the old language. The fix I applied to 8 apps, and five traps that came with it.
2026-09-16 · Android · localization · AppCompatSeven rules I rewrote 13 home-screen widgets against, after placing them next to Samsung's
Putting 13 widgets from 5 apps on a real home screen beside Samsung's weather and wallet widgets showed that "the code exists" and "it looks like a shipped product" are very different. System dark mode, designed empty states, previewLayout and label, dp corner radii, and the theme lag of bitmap widgets.
2026-09-16 · Android · home widgets · UIWhy I put forced in-app updates (IMMEDIATE) into 17 apps: users on old versions earned nothing after the ad SDK swap
When I replaced the ads SDK in July 2026, users who had not updated produced zero revenue. No fix, policy or consent form reaches an old version. How I added Play In-App Update in IMMEDIATE mode to every app, why it can only be verified on a Play-installed build, and what shipped alongside it.
2026-09-16 · Android · Google Play · in-app updateI no longer pick store keywords myself: mine rival titles, verify with autocomplete, measure rank
When I choose the seed words, the result is "my idea of ASO". I switched to scraping the titles of apps that share our search results, keeping only words Play autocomplete confirms as real queries, and measuring where we rank for each. Includes the mistake where one-character prefixes made me miss every core keyword.
2026-09-16 · ASO · Google Play · store optimizationMy AdMob account had zero GDPR consent messages: adding UMP to 21 apps in one day
A new app's release log said "no form(s) configured". The AdMob account had no European-regulation consent message at all, and only 3 of 17 live apps even contained UMP code. The order of operations to create the message, add the code to 14 apps and re-release, plus the traps along the way.
2026-09-16 · AdMob · GDPR · UMPAd revenue is set by session time, not downloads: AdMob numbers across 15 apps
Eleven apps with more monthly users produced 27 times fewer ad impressions than four TV remote apps. Where the gap between 7.68 and 0.18 impressions per user per day comes from, and how it changed the order I do marketing in.
2026-09-16 · AdMob · ad revenue · app operations