我最近在为客户开发的应用中发现了随机崩溃。 该应用程序在3年后现在有大约100,000个活跃用户。
我们已经看到Nexus 4和5的崩溃,两者都是Android 4.4 KitKat。
我们无法在我们自己的运行4.4的Nexus 4和5上重现它。
我们通过我们的支持获得了客户。他告诉我们,在调用新活动时,每次都在同一个地方发生崩溃。他经营的是Dalvik,而不是ART。重置固件后,应用程序运行正常,无法再次重现!
我出于法律原因无法发布源或布局,但是有这个堆栈跟踪:
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
编辑:没有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
在setContentView()中设置的布局包含框架,否则它非常标准且简单。
非常感谢任何输入: - )
答案 0 :(得分:12)
我也遇到了一些我正在维护的代码问题。通过在辅助功能选项中启用TalkBack,我能够始终如一地复制错误。
首先,这是来自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);
}
对我来说,根本原因是一个自定义的View,它已经覆盖了View.isShown(),如下所示:
public boolean isShown(){
return someCondition;
}
这意味着sendAccessibilityEventUncheckedInternal会在继续执行之前运行if(!isShown())检查,即使View有一个null父项,也会导致崩溃。
我原本以为这是一个并发问题,因为我假设isShown()检查确保父级不为null,并且在执行sendAccessibilityEventUncheckedInternal期间对View的父级的引用已被更改。错!
如果您发现类似的问题,特别是在您没有编写的代码中,您可以通过包含超类的isShown()的结果来轻松防止此崩溃(假设您正在更改View的直接子类中的代码):
public boolean isShown(){
return super.isShown() && someCondition;
}
答案 1 :(得分:2)
我的用户遇到了同样的问题,而且似乎是由一个或多个辅助功能选项打开引起的。我的一些用户正在使用Pebble智能手表,它安装了一个辅助功能选项 - 所以它不仅仅是TalkBack等。
View#setFlags()
方法的这一点
if (accessibilityEnabled) {
...
notifyViewAccessibilityStateChangedIfNeeded(
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
如果在将视图附加到视图层次结构(即没有父级)之前执行,则会向您发送以NullPointerException
结尾的兔子洞,因为在我https://github.com/android/platform_frameworks_base/blob/kitkat-mr1-release/core/java/android/view/View.java#L4952的View#sendAccessibilityEventUncheckedInternal()
{/ 1}}:
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
在View#onAttachedToWindow()
被调用时会有一个父级。
您的堆栈跟踪更有问题。您通过XML布局上的属性进入兔子洞。我没有想出你的想法。一个想法是,这必须只发生在应用程序启动时 - 否则几乎所有的XML布局通知都会触发崩溃,因为有很多路径会引导您完成View#setFlags()
。在我的应用程序中,这一点似乎是唯一的崩溃,它发生在应用程序启动时。这不是一个令人愉快的想法,但一种可能性是重新订购事情以便稍后夸大这一观点。