Repository navigation
Keep name-resolved resources from the Android resource shrinker - #100
Merged
Merged
Conversation
CodePush reads CODE_PUSH_APK_BUILD_TIME and the CodePush<Property>
strings and bools through Resources.getIdentifier. The resource shrinker
cannot trace those lookups, and the optimized shrinker that Android
Gradle plugin 9 turns on by default (android.r8.optimizedResourceShrinking)
no longer keeps resources whose names appear as string constants in dex,
which the legacy shrinker's safe mode did.
With shrinkResources enabled on such a build:
- CODE_PUSH_APK_BUILD_TIME is removed, so getBinaryResourcesModifiedTime
throws Resources$NotFoundException ("String resource ID #0x0") whenever
a downloaded package exists on disk. Every user who upgrades the app
with a previously downloaded update crash-loops on cold start, before
clearUpdates() can run.
- CodePushServerUrl is removed, so the runtime silently falls back to
the default server URL and never reaches the configured one.
Ship a tools:keep in the library's res/raw so every consumer is covered
when the AAR's resources merge into the app, the same way Firebase keeps
its own name-resolved strings (firebase_common_keep.xml).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Uh, sorry about the Claude slop below. This just adds a
codepush_keep.xmlso that AGP 9's on-by-default R8 resource shrinking keeps the resources CodePush looks up usinggetIdentifier. I've added one for us locally, but figured I'd upstream it. This fixes two issues which we encountered:CodePushServerUrlis missing which means users can no longer receive code pushes (😱)Problem
CodePush looks several resources up by name through
Resources.getIdentifier:CODE_PUSH_APK_BUILD_TIME, generated bycodepush.gradleas aresValueand read inCodePush.getBinaryResourcesModifiedTimeCodePush<Property>strings and bools from the app's own resources (CodePushServerUrl,CodePushPublicKey,CodePushDeploymentKey, ...) viagetCustomPropertyFromStringsIfExist/getBooleanCustomPropertyFromStringsIfExistThe Android resource shrinker cannot trace
getIdentifiercalls. The legacy shrinker compensated in safe mode by keeping any resource whose name appeared as a string constant in dex, but the optimized shrinker that Android Gradle plugin 9 enables by default (android.r8.optimizedResourceShrinking=true) dropped that heuristic. Expo SDK 58 / React Native 0.88 apps pick this plugin up, and any app withshrinkResources truethen loses these resources from its release build.The consequences:
Crash loop on upgrade.
getBinaryResourcesModifiedTimeruns whenever a downloaded package exists on disk, and the missing resource makes it throw:Every user who upgrades the store build while holding a previously downloaded update crashes on cold start, before
clearUpdates()gets a chance to run. A fresh install is unaffected, which makes it easy to miss in release testing.Silent loss of the server URL. With
CodePushServerUrlremoved, the runtime falls back to the hardcoded default and never contacts the configured server.The resource shrinker emits no warnings for any of this. The only trace is
build/outputs/mapping/release/resources.txtlisting the resources asis not reachable.Fix
Ship a
tools:keepin the library's ownres/raw. Library resources merge into the consuming app before shrinking, so every consumer is covered without changes on their side. This is the same approach Firebase uses for its name-resolved strings (firebase_common_keep.xml).The wildcards cover every
CodePush<Property>the runtime can ask for. Names an app does not define are ignored by the shrinker. The keep file itself is shrunk out of the final APK as unreachable, which is expected.Verification
Reproduced in a React Native 0.88 / Expo SDK 58 app with
minifyEnabled trueandshrinkResources true. Before the keep,resources.txtreported:With an equivalent keep file in the app module, the same build reports both as
reachable from keep xml file,aapt2 dump resourcesshows the values inresources.arsc, and the upgrade crash no longer reproduces.🤖 Generated with Claude Code