I want to share a bug I ran into recently while developing my Android game. The game worked perfectly on my test devices, but when I published it to the Google Play Store, I started getting crash reports from users specifically from Samsung Galaxy S6 Edge and Note 5 devices.
The Crash
After releasing the app, I checked the Play Console and found this scary-looking stack trace:
Caused by: android.content.res.Resources$NotFoundException:
at android.content.res.ResourcesImpl.getValue
...
at mypackagename.GameActivity.onCreate (GameActivity.java:193)
The line causing the crash was this:
setContentView(R.layout.activity_game_5x);
Weird, because the file activity_game_5x.xml
was sitting right there in my res/layout/
folder. Or so I thought…
My Layout Switching Code
In my game, I was switching layouts depending on screen size. Here’s the block of code I wrote:
if (sWidth > 480 && screenInches >= 4 && screenInches <= 5) {
setContentView(R.layout.activity_game_4x);
} else if (screenInches >= 5 && screenInches <= 6.5) {
setContentView(R.layout.activity_game_5x); // Line 193
} else if (screenInches > 6.5 && screenInches < 9) {
setContentView(R.layout.activity_game_7x);
} else {
setContentView(R.layout.activity_game);
}
This worked fine during testing. But clearly, something wasn’t right when the app hit real-world devices.
What Went Wrong?
After researching and experimenting, I figured out a few possible causes for this error:
Layout Qualifier
Maybe activity_game_5x.xml
was placed inside a folder like layout-sw600dp/
or layout-large/
, so devices that didn’t match those screen configurations couldn’t find it.
File Name
Even a minor typo in the filename or an XML error inside the layout file could cause the resource to be excluded from the build.
ProGuard or Build Settings
If you’re using ProGuard or app bundles with APK splits, there’s a chance some layouts aren’t included in certain device configurations.
The Fix Defensive Coding
To solve this and make sure the app never crashes because of a missing layout, I wrapped the whole logic in a try-catch block:
try {
if (sWidth > 480 && screenInches >= 4 && screenInches <= 5) {
setContentView(R.layout.activity_game_4x);
} else if (screenInches >= 5 && screenInches <= 6.5) {
setContentView(R.layout.activity_game_5x);
} else if (screenInches > 6.5 && screenInches < 9) {
setContentView(R.layout.activity_game_7x);
} else {
setContentView(R.layout.activity_game);
}
} catch (Resources.NotFoundException e) {
e.printStackTrace(); // For debug purposes
setContentView(R.layout.activity_game); // Fallback to default
}
This way, if any of the layouts fail to load, the app won’t crash — it just uses the default layout.
User Feedback + Logging
To take it one step further, I created a helper method that includes logging and a Toast message for users (and me):
private void loadGameLayout(float screenInches, int sWidth) {
try {
if (sWidth > 480 && screenInches >= 4 && screenInches <= 5) {
setContentView(R.layout.activity_game_4x);
} else if (screenInches >= 5 && screenInches <= 6.5) {
setContentView(R.layout.activity_game_5x);
} else if (screenInches > 6.5 && screenInches < 9) {
setContentView(R.layout.activity_game_7x);
} else {
setContentView(R.layout.activity_game);
}
} catch (Resources.NotFoundException e) {
Log.e("GameActivity", "Layout not found, using default", e);
Toast.makeText(this, "Error loading layout, using default.", Toast.LENGTH_SHORT).show();
setContentView(R.layout.activity_game);
}
}
Then in my onCreate()
method, I just call:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
float screenInches = calculateScreenInches();
int screenWidth = getResources().getDisplayMetrics().widthPixels;
loadGameLayout(screenInches, screenWidth);
}
Pro Tips I Learn
- Always validate all XML layouts before release.
- Avoid placing layouts only in
layout-sw*
or other resource-qualified folders unless absolutely necessary. - Use a default fallback layout like
activity_game.xml
that always exists. - Add runtime checks to verify a layout exists using
getResources().getIdentifier(...)
.
Final Thought
This bug was a great reminder that just because your app works on your test devices doesn’t mean it’ll work on every device. The Android ecosystem is incredibly diverse, and it’s worth writing your code to fail gracefully especially with things like dynamic layouts.
I hope this helped anyone else facing mysterious Resources$NotFoundException
crashes. Defensive programming saved me a lot of headache and bad reviews.