Android App Bundle Crashing on ResourceNotFound
Android App Bundle has been a required format for Play Store app uploads for years now, but there is still an important gotcha that requires special handling. When a user restores a backup or sideloads an APK that was derived from the AAB format, that APK might not have the resources intended for their new device.
Let's walk through an example. The user downloads the app on their phone, and some time passes. They decide to upgrade their phone to one with better specs. The APK downloaded to their old phone only has xhdpi
resources and their new phone has a higher DPI density which makes it expect xxhdpi
resources. When they restore a backup of the APK to their new phone it crashes on attempting to load any density based resource.
Another example is for apps that have put in the work to translate their experience into other languages. If you ship an AAB that allows splits on translations and the user changes their app specific or system language, the app will crash on attempting to load such resources.
Generally user experiencing this would never use the app again since the crash happens on app start and is reproducible even after wiping local application data. They wouldn't necessarily think to uninstall and reinstall to fix the issue, which is actually the only resolution from this state. Therefore we wrote a simple runtime check to be used before starting our single Activity.
fun canLoadResources(@DrawableRes resourceId: Int): Boolean {
val result = try {
ContextCompat.getDrawable(baseContext, resourceId)
} catch (ex: Exception) {
null
}
return result != null
}
Therefore we wrote a couple lines of code that would just redirect them to a different Activity.
We also want to immediately redirect the user to a different Activity that never makes use of any resources that could be split for a particular APK. Regardless of launchMode, using the following combination of Intent flags should ensure we leave no chance of any remaining separate Activity.
fun redirectToInvalidApkScreen() {
startActivity(
Intent(baseContext,
InvalidApkActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or
Intent.FLAG_ACTIVITY_CLEAR_TASK
}
)
}
Where should we add this check and redirect? In our experience the user typically encounters this type of crash in the launcher activity's onCreate method:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
setTheme(co.hinge.common.design.resources.R.style.HingeTheme)
super.onCreate(savedInstanceState)
+ if (!canLoadResources(R.drawable.example_drawable)) {
+ redirectToInvalidApkScreen()
+ return
+ }
setContentView(ui.root)
}
}
Assuming you've written InvalidApkActivity
in a way that doesn't use resources you never have to think about this issue again.