Android 4.4 KitKat随机崩溃

时间:2022-01-16 15:31:33

EDIT: Before down-voting and implying things, please understand I cannot reproduce this error. This happens constantly on certain devices which I do not have access to, but not after a firmware reset!

I recently discovered random crashes in an app I am developing for a customer. The app has roughly 100.000 active users now after 3 years.

我最近在为客户开发的应用程序中发现了随机崩溃。该应用程序在3年后现在拥有大约100,000名活跃用户。

We've seen the crash on Nexus 4 and 5, both with Android 4.4 KitKat.

我们已经看到了Nexus 4和5的崩溃,两者都是Android 4.4 KitKat。

We cannot reproduce it on our own Nexus 4 and 5 running 4.4.

我们无法在我们自己的运行4.4的Nexus 4和5上重现它。

We've had a customer through our support. He told us the crash happens every time at the same place when invoking a new activity. He was running Dalvik, not ART. After resetting the firmware the app worked fine and could not reproduce it again!

我们通过我们的支持获得了客户。他告诉我们,在调用新活动时,每次都在同一个地方发生崩溃。他在运行Dalvik,而不是ART。重置固件后,应用程序运行正常,无法再次重现!

I cannot post the source or layout for legal reasons, but have this stacktrace:

我出于法律原因无法发布源或布局,但是有这个堆栈跟踪:

java.lang.RuntimeException: Unable to start activity ComponentInfo{xx.xxx.xxxxx.xxx.xxxxxx.prod/xx.xxx.xxxxx.xxx.PaymentsActivity}: java.lang.NullPointerException
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2176)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2226)
at android.app.ActivityThread.access$700(ActivityThread.java:135)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1397)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4998)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:777)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:593)
at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:126)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.NullPointerException
at android.view.View.sendAccessibilityEventUncheckedInternal(View.java:4938)
at android.view.View.sendAccessibilityEventUnchecked(View.java:4919)
at android.view.View$SendViewStateChangedAccessibilityEvent.run(View.java:19433)
at android.view.View$SendViewStateChangedAccessibilityEvent.runOrPost(View.java:19465)
at android.view.View.notifyViewAccessibilityStateChangedIfNeeded(View.java:7265)
at android.view.View.setFlags(View.java:8990)
at android.view.View.setVisibility(View.java:6020)
at android.view.LayoutInflater.parseInclude(LayoutInflater.java:859)
at de.robv.android.xposed.XposedBridge.invokeOriginalMethodNative(Native Method)
at de.robv.android.xposed.XposedBridge.handleHookedMethod(XposedBridge.java:547)
at android.view.LayoutInflater.parseInclude(Native Method)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:745)
at android.view.LayoutInflater.inflate(LayoutInflater.java:492)
at de.robv.android.xposed.XposedBridge.invokeOriginalMethodNative(Native Method)
at de.robv.android.xposed.XposedBridge.handleHookedMethod(XposedBridge.java:547)
at android.view.LayoutInflater.inflate(Native Method)
at android.view.LayoutInflater.inflate(LayoutInflater.java:397)
at android.view.LayoutInflater.inflate(LayoutInflater.java:353)
at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:290)
at android.app.Activity.setContentView(Activity.java:1928)
at xx.xxx.xxxxx.xxx.StandardActivity.setContentView(StandardActivity.java:289)
at xx.xxx.xxxxx.xxx.PaymentsActivity.onCreate(PaymentsActivity.java:61)
at android.app.Activity.performCreate(Activity.java:5243)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2140)
... 12 more

EDIT: Second stacktrace without xposed

编辑:没有xposed的第二个堆栈跟踪

java.lang.RuntimeException: Unable to start activity ComponentInfo{xx.xxx.xxxxx.xxx.xxxxx.prod/xx.xxx.xxxxx.xxx.PaymentsActivity}: java.lang.NullPointerException
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2176)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2226)
at android.app.ActivityThread.access$700(ActivityThread.java:135)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1397)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4998)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:777)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:593)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.NullPointerException
at android.view.View.sendAccessibilityEventUncheckedInternal(View.java:4938)
at android.view.View.sendAccessibilityEventUnchecked(View.java:4919)
at android.view.View$SendViewStateChangedAccessibilityEvent.run(View.java:19433)
at android.view.View$SendViewStateChangedAccessibilityEvent.runOrPost(View.java:19465)
at android.view.View.notifyViewAccessibilityStateChangedIfNeeded(View.java:7265)
at android.view.View.setFlags(View.java:8990)
at android.view.View.setVisibility(View.java:6020)
at android.view.LayoutInflater.parseInclude(LayoutInflater.java:859)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:745)
at android.view.LayoutInflater.inflate(LayoutInflater.java:492)
at android.view.LayoutInflater.inflate(LayoutInflater.java:397)
at android.view.LayoutInflater.inflate(LayoutInflater.java:353)
at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:290)
at android.app.Activity.setContentView(Activity.java:1928)
at xx.xxx.xxxxx.xxx.StandardActivity.setContentView(StandardActivity.java:289)
at xx.xxx.xxxxx.xxx.PaymentsActivity.onCreate(PaymentsActivity.java:61)
at android.app.Activity.performCreate(Activity.java:5243)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2140)
... 11 more

The layout being set in setContentView() contains frames, else it's pretty standard and plain.

在setContentView()中设置的布局包含框架,否则它非常标准和简单。

Any input is highly appreciated :-)

任何输入都非常感谢:-)

2 个解决方案

#1


11  

I've also run into this same problem with some code I was maintaining. I was able to consistently replicate the bug by enabling TalkBack in the accessibility options.

我也遇到了一些我正在维护的代码问题。通过在辅助功能选项中启用TalkBack,我能够始终如一地复制错误。

First, here's the method from View.java where the null reference that caused the crash was used, from the KitKat release of Android:

首先,这是来自View.java的方法,其中使用了导致崩溃的空引用,来自Android的KitKat版本:

void sendAccessibilityEventUncheckedInternal(AccessibilityEvent event) {
    if (!isShown()) {
        return;
    }
    onInitializeAccessibilityEvent(event);
    // Only a subset of accessibility events populates text content.
    if ((event.getEventType() & POPULATING_ACCESSIBILITY_EVENT_TYPES) != 0) {
        dispatchPopulateAccessibilityEvent(event);
    }
    // In the beginning we called #isShown(), so we know that getParent() is not null.
    getParent().requestSendAccessibilityEvent(this, event);
}

For me, the root cause turned out to be a custom View which had overridden View.isShown() like so:

对我来说,根本原因结果是一个自定义View,它已经覆盖了View.isShown(),如下所示:

public boolean isShown(){
  return someCondition;
}

This meant that sendAccessibilityEventUncheckedInternal would run past the if(!isShown()) check that it makes before proceeding even when the View had a null parent, and so caused the crash.

这意味着sendAccessibilityEventUncheckedInternal将在继续执行之前运行if(!isShown())检查,即使View具有null父级,也会导致崩溃。

I had originally thought it was a concurrency problem, because I assumed the isShown() check had ensured the parent wasn't null and that the reference to the View's parent had been changed during the execution of sendAccessibilityEventUncheckedInternal. Wrong!

我原本以为这是一个并发问题,因为我假设isShown()检查确保父级不为null,并且在执行sendAccessibilityEventUncheckedInternal期间对View的父级的引用已被更改。错误!

If you find a similar problem, especially in code you didn't write, you can prevent this crash pretty easily by including the result of the superclass's isShown() (assuming you are changing code in a direct subClass of View):

如果您发现类似的问题,特别是在您没有编写的代码中,您可以通过包含超类的isShown()的结果来轻松防止此崩溃(假设您正在更改View的直接子类中的代码):

public boolean isShown(){
  return super.isShown() && someCondition;
}

#2


2  

My users have been running into the same problem and it appears to be caused by one or more accessibility options being turned on. Some of my users were using the Pebble smart watch which installs an accessibility option - so it's not just TalkBack, etc.

我的用户遇到了同样的问题,似乎是由一个或多个辅助功能选项打开引起的。我的一些用户使用的Pebble智能手表安装了辅助功能选项 - 所以它不仅仅是TalkBack等。

The diagnosis

Take a look at this bit of KitKat's View#setFlags() method at https://github.com/android/platform_frameworks_base/blob/kitkat-mr1-release/core/java/android/view/View.java#L9006

在https://github.com/android/platform_frameworks_base/blob/kitkat-mr1-release/core/java/android/view/View.java#L9006上查看KitKat的View#setFlags()方法。

if (accessibilityEnabled) {
  ...
  notifyViewAccessibilityStateChangedIfNeeded(
                        AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}

that sends you down the rabbit hole ending in a NullPointerException if it's executed before the View is attached to the view hierarchy (i.e. is has no parent) because in View#sendAccessibilityEventUncheckedInternal() at https://github.com/android/platform_frameworks_base/blob/kitkat-mr1-release/core/java/android/view/View.java#L4952 we have:

如果它在View附加到视图层次结构之前执行(即没有父级),则会将您发送到以NullPointerException结尾的兔子洞,因为在View#sendAccessibilityEventUncheckedInternal()中位于https://github.com/android/platform_frameworks_base/ blob / kitkat-mr1-release / core / java / android / view / View.java#L4952我们有:

getParent().requestSendAccessibilityEvent(this, event);

My workaround (which looks like it won't work for you)

For my app, I am creating a View subclass programmatically and was calling View#setOnClickListener() in the constructor. Instead, I now call View#setOnClickListener() from

对于我的应用程序,我以编程方式创建一个View子类,并在构造函数中调用View#setOnClickListener()。相反,我现在调用View#setOnClickListener()

@Override
protected void onAttachedToWindow() {
    super.onAttachedToWindow();
    /* Due to a bug in how Android 4.4 handles accessibility options,
     * we can't set the onClick listener until this View has a parent or we will
     * get an NPE. */
    setOnClickListener(this);
}

It works because this View will have a parent by the time View#onAttachedToWindow() gets called.

它的工作原理是因为在调用View#onAttachedToWindow()时,此View将具有父级。

Your stack trace is more problematic though. You're falling into the rabbit hole via attributes on an XML layout. I haven't come up with an idea for you. One thought is that this must only happen at app startup - otherwise virtually all inflations of XML layouts would trigger the crash because there are so many paths that take you through View#setFlags(). In my app, this one spot appears to be the only crash and it happens at app startup. It's not a pleasant idea but one possibility is to re-order things to inflate this view later.

但是,堆栈跟踪更成问题。你是通过XML布局上的属性陷入兔子洞的。我没有想出你的想法。一个想法是,这必须只在app启动时发生 - 否则几乎所有XML布局的通知都会触发崩溃,因为有太多的路径会引导你通过View#setFlags()。在我的应用程序中,这一点似乎是唯一的崩溃,它发生在应用程序启动时。这不是一个令人愉快的想法,但一种可能性是重新订购事物以便稍后膨胀这个观点。

#1


11  

I've also run into this same problem with some code I was maintaining. I was able to consistently replicate the bug by enabling TalkBack in the accessibility options.

我也遇到了一些我正在维护的代码问题。通过在辅助功能选项中启用TalkBack,我能够始终如一地复制错误。

First, here's the method from View.java where the null reference that caused the crash was used, from the KitKat release of Android:

首先,这是来自View.java的方法,其中使用了导致崩溃的空引用,来自Android的KitKat版本:

void sendAccessibilityEventUncheckedInternal(AccessibilityEvent event) {
    if (!isShown()) {
        return;
    }
    onInitializeAccessibilityEvent(event);
    // Only a subset of accessibility events populates text content.
    if ((event.getEventType() & POPULATING_ACCESSIBILITY_EVENT_TYPES) != 0) {
        dispatchPopulateAccessibilityEvent(event);
    }
    // In the beginning we called #isShown(), so we know that getParent() is not null.
    getParent().requestSendAccessibilityEvent(this, event);
}

For me, the root cause turned out to be a custom View which had overridden View.isShown() like so:

对我来说,根本原因结果是一个自定义View,它已经覆盖了View.isShown(),如下所示:

public boolean isShown(){
  return someCondition;
}

This meant that sendAccessibilityEventUncheckedInternal would run past the if(!isShown()) check that it makes before proceeding even when the View had a null parent, and so caused the crash.

这意味着sendAccessibilityEventUncheckedInternal将在继续执行之前运行if(!isShown())检查,即使View具有null父级,也会导致崩溃。

I had originally thought it was a concurrency problem, because I assumed the isShown() check had ensured the parent wasn't null and that the reference to the View's parent had been changed during the execution of sendAccessibilityEventUncheckedInternal. Wrong!

我原本以为这是一个并发问题,因为我假设isShown()检查确保父级不为null,并且在执行sendAccessibilityEventUncheckedInternal期间对View的父级的引用已被更改。错误!

If you find a similar problem, especially in code you didn't write, you can prevent this crash pretty easily by including the result of the superclass's isShown() (assuming you are changing code in a direct subClass of View):

如果您发现类似的问题,特别是在您没有编写的代码中,您可以通过包含超类的isShown()的结果来轻松防止此崩溃(假设您正在更改View的直接子类中的代码):

public boolean isShown(){
  return super.isShown() && someCondition;
}

#2


2  

My users have been running into the same problem and it appears to be caused by one or more accessibility options being turned on. Some of my users were using the Pebble smart watch which installs an accessibility option - so it's not just TalkBack, etc.

我的用户遇到了同样的问题,似乎是由一个或多个辅助功能选项打开引起的。我的一些用户使用的Pebble智能手表安装了辅助功能选项 - 所以它不仅仅是TalkBack等。

The diagnosis

Take a look at this bit of KitKat's View#setFlags() method at https://github.com/android/platform_frameworks_base/blob/kitkat-mr1-release/core/java/android/view/View.java#L9006

在https://github.com/android/platform_frameworks_base/blob/kitkat-mr1-release/core/java/android/view/View.java#L9006上查看KitKat的View#setFlags()方法。

if (accessibilityEnabled) {
  ...
  notifyViewAccessibilityStateChangedIfNeeded(
                        AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}

that sends you down the rabbit hole ending in a NullPointerException if it's executed before the View is attached to the view hierarchy (i.e. is has no parent) because in View#sendAccessibilityEventUncheckedInternal() at https://github.com/android/platform_frameworks_base/blob/kitkat-mr1-release/core/java/android/view/View.java#L4952 we have:

如果它在View附加到视图层次结构之前执行(即没有父级),则会将您发送到以NullPointerException结尾的兔子洞,因为在View#sendAccessibilityEventUncheckedInternal()中位于https://github.com/android/platform_frameworks_base/ blob / kitkat-mr1-release / core / java / android / view / View.java#L4952我们有:

getParent().requestSendAccessibilityEvent(this, event);

My workaround (which looks like it won't work for you)

For my app, I am creating a View subclass programmatically and was calling View#setOnClickListener() in the constructor. Instead, I now call View#setOnClickListener() from

对于我的应用程序,我以编程方式创建一个View子类,并在构造函数中调用View#setOnClickListener()。相反,我现在调用View#setOnClickListener()

@Override
protected void onAttachedToWindow() {
    super.onAttachedToWindow();
    /* Due to a bug in how Android 4.4 handles accessibility options,
     * we can't set the onClick listener until this View has a parent or we will
     * get an NPE. */
    setOnClickListener(this);
}

It works because this View will have a parent by the time View#onAttachedToWindow() gets called.

它的工作原理是因为在调用View#onAttachedToWindow()时,此View将具有父级。

Your stack trace is more problematic though. You're falling into the rabbit hole via attributes on an XML layout. I haven't come up with an idea for you. One thought is that this must only happen at app startup - otherwise virtually all inflations of XML layouts would trigger the crash because there are so many paths that take you through View#setFlags(). In my app, this one spot appears to be the only crash and it happens at app startup. It's not a pleasant idea but one possibility is to re-order things to inflate this view later.

但是,堆栈跟踪更成问题。你是通过XML布局上的属性陷入兔子洞的。我没有想出你的想法。一个想法是,这必须只在app启动时发生 - 否则几乎所有XML布局的通知都会触发崩溃,因为有太多的路径会引导你通过View#setFlags()。在我的应用程序中,这一点似乎是唯一的崩溃,它发生在应用程序启动时。这不是一个令人愉快的想法,但一种可能性是重新订购事物以便稍后膨胀这个观点。