App has over 20000 monthly active users. It's been available on google play for months. After I've recently switched from distribution with .apk to distribution with .aab, I've started receiving random crashes on crashlytics and google play store. No other significant changes were made in the build that introduced the crashes.
The crash happens on the very first screen of the app, while inflating xml layout. The xml layout in question is a simple splash screen that only contains one image view and one textview. The imageview is android.widget.ImageView, not compat version, and it displays png image, not a vector image. The image is present in all drawable folder variations: drawable, drawable-mdpi, ..., drawable-xxxhdpi.
Fatal Exception: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.company/com.company.ui.splash.SplashActivity}: android.view.InflateException: Binary XML file line #14: Binary XML file line #14: Error inflating class ImageView
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2913)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:495)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Caused by android.view.InflateException: Binary XML file line #14: Binary XML file line #14: Error inflating class ImageView
Caused by android.view.InflateException: Binary XML file line #14: Error inflating class ImageView
Caused by android.content.res.Resources$NotFoundException: Drawable (missing name) with resource ID #0x7f0800b2
Caused by android.content.res.Resources$NotFoundException: Unable to find resource ID #0x7f0800b2
at android.content.res.ResourcesImpl.getResourceName(ResourcesImpl.java:255)
at android.content.res.ResourcesImpl.loadDrawableForCookie(ResourcesImpl.java:785)
at android.content.res.ResourcesImpl.loadDrawable(ResourcesImpl.java:631)
at android.content.res.Resources.loadDrawable(Resources.java:897)
at android.content.res.TypedArray.getDrawableForDensity(TypedArray.java:955)
at android.content.res.TypedArray.getDrawable(TypedArray.java:930)
at android.widget.ImageView.<init>(ImageView.java:189)
at android.widget.ImageView.<init>(ImageView.java:172)
at android.support.v7.widget.AppCompatImageView.<init>(AppCompatImageView.java:71)
at android.support.v7.widget.AppCompatImageView.<init>(AppCompatImageView.java:67)
at android.support.v7.app.AppCompatViewInflater.createImageView(AppCompatViewInflater.java:181)
at android.support.v7.app.AppCompatViewInflater.createView(AppCompatViewInflater.java:105)
at android.support.v7.app.AppCompatDelegateImplV9.createView(AppCompatDelegateImplV9.java:1035)
at android.support.v7.app.AppCompatDelegateImplV9.onCreateView(AppCompatDelegateImplV9.java:1092)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:772)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:730)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:863)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:824)
at android.view.LayoutInflater.inflate(LayoutInflater.java:515)
at android.view.LayoutInflater.inflate(LayoutInflater.java:423)
at android.view.LayoutInflater.inflate(LayoutInflater.java:374)
at android.support.v7.app.AppCompatDelegateImplV9.setContentView(AppCompatDelegateImplV9.java:287)
at android.support.v7.app.AppCompatActivity.setContentView(AppCompatActivity.java:139)
at com.company.ui.splash.SplashActivity.onCreate(SplashActivity.java:58)
at android.app.Activity.performCreate(Activity.java:7136)
at android.app.Activity.performCreate(Activity.java:7127)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2893)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:495)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Part of xml file that causes the crash:
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/center"
android:layout_centerHorizontal="true"
android:src="@drawable/logo" />
The crash happens on all android versions, from 4.1.2 up to 9.0.
Aside from other devices, I've also received crashes from Google Pixel and Nexus 5X, both non-rooted. I happen to own both devices. I've tried installing my app on them from google play and from sideloading services such as pureapk, but I wasn't able to reproduce the crash.
The question is similar to Android App Bundle introduces Resource Not found crash in Android app , but in that question, author was able to solve his problems by using vector drawable compat. This is not my case.
It appears that the entire resource folder is missing from the apk somehow, although testing this assumption is hard. I cannot reproduce the issue so I'll have o re-destribute the app and wait for a couple of days to see how the crash has changed, and I'd rather not do testing on live users.
When this bug happens, it usually happens several times in the row for the same devices, which leads me to believe that users who get this bug cannot ever launch the app. Also, I know that some of the users who previously used the app cannot use it anymore.
So, to sum this up:
1. Crash started to appear right after switching to android application bundle distribution on google play
2. App crashes on its first attempt to recover drawable resource - a simple png image
3. The crash is not android-version specific; it happens on both rooted and non-rooted devices
4. If the user gets this bug, he's probably stuck forever
What is causing this crash? Is there a workaround?
==========
Update: After reading the answer below I've concluded that the only workaround is to detect sideload installation and then open activity without any drawable resources or styles with links to Google Play and official site with old-school apk file. The user can then re-download app from another source.
This is the code I use to detect if app was sideloaded (you might need to remove nativeLibrariesPresent
part if there are no native libraries in your app):
private fun isValidInstallation(): Boolean {
var resourcesPresent: Boolean
try {
// Any drawable id will suffice
val logo = ResourcesCompat.getDrawable(resources, R.drawable.logo_white, null)
resourcesPresent = logo != null
} catch (e: Exception) {
resourcesPresent = false
}
if (!resourcesPresent) {
Timber.e("No drawable resources detected inside app")
}
var nativeLibrariesPresent: Boolean
try {
val nativeLibraryDir = File(applicationInfo.nativeLibraryDir)
val primaryNativeLibraries = nativeLibraryDir.list()
nativeLibrariesPresent = primaryNativeLibraries.isNotEmpty()
} catch (e: Exception) {
nativeLibrariesPresent = false
}
if (!nativeLibrariesPresent) {
Timber.e("No native libraries detected inside app")
}
return resourcesPresent && nativeLibrariesPresent
}
You'll want to start alternative activity before you do anything inside your main activity:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (!isValidInstallation()) {
val intent = Intent(this, InvalidInstallationActivity::class.java)
startActivity(intent)
finish()
return
}
setContentView(R.layout.activity_main)
...
InvalidInstallationActivity can use xml layout and it can use string resources if you don't split your aab by language (language { enableSplit = false }
), but it cannot use any drawable resources.
@drawable/logo
is only present for certain screen densities, and therefore isn't included for e.g. xxhdpi devices – Freezedrying