Android 4.4 KitKat 随机崩溃

Posted

技术标签:

【中文标题】Android 4.4 KitKat 随机崩溃【英文标题】:Android 4.4 KitKat random crash 【发布时间】:2013-12-24 03:23:03 【问题描述】:

编辑:在否决和暗示事情之前,请理解我无法重现此错误。这在我无法访问的某些设备上经常发生,但在固件重置后不会发生!

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

我们已经看到 Nexus 4 和 5 上的崩溃,两者都使用 android 4.4 KitKat。

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

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

出于法律原因,我无法发布源代码或布局,但有此堆栈跟踪:

java.lang.RuntimeException: Unable to start activity ComponentInfoxx.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

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

java.lang.RuntimeException: Unable to start activity ComponentInfoxx.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

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

非常感谢任何输入 :-)

【问题讨论】:

问题可能出在布局上,也许this页面可以帮到你。检查你使用的东西是否在 4.4 中发生了变化。没有代码有点难。 一个 NPE 应该不难发现和解决 这样的框架代码中的 NPE 可能更难找到和解决,因为您无法直接控制该代码。虽然您可以从 AOSP 获取源代码,以便跟踪代码流并更好地了解 NPE 可能发生的原因。 另外,我不确定为什么对这个问题投反对票。对我来说,这似乎是一个完全合理的问题。 啊,好的。如果您在其他地方看到过该错误(即堆栈跟踪中没有 xposed 条目),那么这不太可能是问题所在。在这种情况下,我建议获取 AOSP 源代码并查看堆栈跟踪中提到的位置。您可以从那里向后工作,尝试确定此时会导致 NPE 的条件。 【参考方案1】:

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

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

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);

对我来说,根本原因是一个自定义视图,它像这样覆盖了 View.isShown():

public boolean isShown()
  return someCondition;

这意味着 sendAccessibilityEventUncheckedInternal 会运行通过 if(!isShown()) 检查,即使 View 有一个 null 父级,也会导致崩溃。

我最初认为这是一个并发问题,因为我假设 isShown() 检查确保父级不为空,并且在执行 sendAccessibilityEventUncheckedInternal 期间对视图的父级的引用已更改。错了!

如果您发现类似的问题,尤其是在您未编写的代码中,您可以通过包含超类的 isShown() 的结果来很容易地防止这种崩溃(假设您正在更改 View 的直接子类中的代码):

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

【讨论】:

你摇滚,@Blammo 船长!实际上有一个 isShown() 的覆盖我仍然无法重现它,但会应用你建议的修复来查看它是否修复它:-) 这解决了一个类似的问题,因为我也创建了一个自定义 View 并覆盖了 isShown() 方法。 这个回复救了我……显然我遇到了同样的问题。我的应用程序在三星设备上运行良好,但在 LGG3 设备上崩溃了!然而,这个修复解决了我的问题。谢谢! 完成!我再也没有听到或看到任何与这个问题相关的东西,所以它一定有效:) 谢谢老兄。编码愉快!【参考方案2】:

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

诊断

在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);

这会将您送入以NullPointerException 结尾的兔子洞如果它在视图附加到视图层次结构之前执行(即没有父级),因为在View#sendAccessibilityEventUncheckedInternal() @987654322 @我们有:

getParent().requestSendAccessibilityEvent(this, event);

我的解决方法(看起来对你不起作用)

对于我的应用程序,我正在以编程方式创建一个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);

之所以有效,是因为在调用 View#onAttachedToWindow() 时,此 View 将有一个父级。

您的堆栈跟踪问题更大。您正在通过 XML 布局上的属性掉入兔子洞。我还没给你想出主意。一种想法是这只能在应用程序启动时发生 - 否则几乎所有 XML 布局的膨胀都会触发崩溃,因为有太多的路径可以让你通过View#setFlags()。在我的应用程序中,这一点似乎是唯一的崩溃,它发生在应用程序启动时。这不是一个令人愉快的想法,但一种可能是重新排序以在以后扩展此视图。

【讨论】:

太棒了,@bpenrod !我猜这也是一个并发问题:void sendAccessibilityEventUncheckedInternal(AccessibilityEvent event) if (!isShown()) //<-- parent is not null return; ... // In the beginning we called #isShown(), so we know that getParent() is not null. getParent().requestSendAccessibilityEvent(this, event); //<-- parent is now null, same thread :-| (编辑:^我不知道如何正确格式化该代码)或者调用路径中的某个人已将其设置为空。我需要再挖一些。 Method added in comment above

以上是关于Android 4.4 KitKat 随机崩溃的主要内容,如果未能解决你的问题,请参考以下文章

透明状态栏 - Android 4.4 (KitKat) 之前

无法在 Android 4.4 KitKat 上快速关闭 ChunkedInputStream

android webview中的HTML文件输入(android 4.4,kitkat)

在 android 4.4+ 或 kitkat 中隐藏状态栏

利用 Android 4.4 KitKat 中的半透明状态栏

利用 Android 4.4 KitKat 中的半透明状态栏