【发布时间】:2019-02-27 09:49:29
【问题描述】:
应用每月有超过 20000 名活跃用户。 它已经在 google play 上发布了几个月。 在我最近从使用 .apk 的分发切换到使用 .aab 的分发后,我开始在 crashlytics 和 google play store 上收到随机崩溃。在引入崩溃的构建中没有进行其他重大更改。
崩溃发生在应用程序的第一个屏幕上,同时扩展了 xml 布局。有问题的 xml 布局是一个简单的初始屏幕,仅包含一个图像视图和一个文本视图。 imageview 是 android.widget.ImageView,不是兼容版本,它显示 png 图像,而不是矢量图像。该图像存在于所有可绘制文件夹变体中: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)
导致崩溃的部分xml文件:
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/center"
android:layout_centerHorizontal="true"
android:src="@drawable/logo" />
崩溃发生在所有 android 版本上,从 4.1.2 到 9.0。
除了其他设备外,我还收到了来自 Google Pixel 和 Nexus 5X 的崩溃,这两个设备均未获得 root 权限。我碰巧拥有这两种设备。我尝试通过 google play 和侧载服务(例如 pureapk)在它们上安装我的应用程序,但我无法重现崩溃。
这个问题类似于Android App Bundle introduces Resource Not found crash in Android app,但是在那个问题中,作者能够通过使用vector drawable compat来解决他的问题。这不是我的情况。
似乎整个资源文件夹都以某种方式从 apk 中丢失,尽管很难测试这个假设。我无法重现该问题,因此我将重新发布该应用程序并等待几天看看崩溃发生了什么变化,我宁愿不对实时用户进行测试。
当这个错误发生时,它通常会在同一台设备上连续发生多次,这让我相信遇到这个错误的用户永远无法启动应用程序。另外,我知道一些以前使用该应用程序的用户不能再使用它了。
所以,总结一下:
1. 在google play上切换到android应用程序包分发后立即开始出现崩溃
2. 应用程序在首次尝试恢复可绘制资源时崩溃 - 一个简单的 png 图像
3.崩溃不是android-version特定的;它发生在有根和无根设备上
4.如果用户遇到这个bug,他可能会永远卡住
是什么导致了这个崩溃?有解决办法吗?
===========
更新:阅读下面的答案后,我得出的结论是,唯一的解决方法是检测侧载安装,然后在没有任何可绘制资源或样式的情况下打开活动,并链接到 Google Play 和带有旧的官方网站-学校 apk 文件。然后用户可以从其他来源重新下载应用程序。
这是我用来检测应用程序是否被侧载的代码(如果您的应用程序中没有本机库,您可能需要删除 nativeLibrariesPresent 部分):
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
}
在您在主要活动中执行任何操作之前,您需要开始替代活动:
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 可以使用 xml 布局,如果您不按语言 (language { enableSplit = false }) 拆分 aab,它可以使用字符串资源,但它不能使用任何可绘制资源。
【问题讨论】:
-
我不完全熟悉拆分 apk 的工作原理,但我想知道
@drawable/logo是否仅适用于某些屏幕密度,因此不包括用于例如xxhdpi 设备 -
@BenP。徽标适用于所有密度。此外,我已经尝试在不同的设备上安装该应用程序的所有版本,并且从未崩溃过。我还验证了即使你只在你的 aab 中放了 1 个版本的图像(比如只有 xxhdpi 版本),生成的 apk 仍然可以在更低和更高密度的设备上运行。
-
@yuval 你是对的,另一个问题是早些时候提出的,但在我的问题得到答案一个月后得到了相同的答案。那我就结束我的问题了。
-
@mochadwi 是的,在我添加 InvalidInstallationActivity 后,这个崩溃从 crashlytics 中消失了。
标签: android google-play