Android ViewGroup 崩溃: 尝试从一个空对象引用上的‘ int Android.view. mViewFlags’字段读取

我们已经发现了几个由后端日志监视报告的此类崩溃案例。似乎这些崩溃并不与特定的用户体验失败有关。从报告来看,没有迹象表明我们自己的类是如何参与进来的(没有迹象表明我们的任何类名)。下面是一个典型的撞车事故例子:

java.lang.NullPointerException: Attempt to read from field 'int android.view.View.mViewFlags' on a null object reference
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3357)
at android.view.View.updateDisplayListIfDirty(View.java:14288)
at android.view.View.getDisplayList(View.java:14315)
at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3549)
at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3528)
at android.view.View.updateDisplayListIfDirty(View.java:14253)
at android.view.View.getDisplayList(View.java:14315)
at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3549)
at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3528)
at android.view.View.updateDisplayListIfDirty(View.java:14253)
at android.view.View.getDisplayList(View.java:14315)
at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3549)
at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3528)
at android.view.View.updateDisplayListIfDirty(View.java:14253)
at android.view.View.getDisplayList(View.java:14315)
at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3549)
at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3528)
at android.view.View.updateDisplayListIfDirty(View.java:14253)
at android.view.View.getDisplayList(View.java:14315)
at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3549)
at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3528)
at android.view.View.updateDisplayListIfDirty(View.java:14253)
at android.view.View.getDisplayList(View.java:14315)
at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3549)
at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3528)
at android.view.View.updateDisplayListIfDirty(View.java:14253)
at android.view.View.getDisplayList(View.java:14315)
at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3549)
at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3528)
at android.view.View.updateDisplayListIfDirty(View.java:14253)
at android.view.View.getDisplayList(View.java:14315)
at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3549)
at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3528)
at android.view.View.updateDisplayListIfDirty(View.java:14253)
at android.view.View.getDisplayList(View.java:14315)
at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3549)
at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3528)
at android.view.View.updateDisplayListIfDirty(View.java:14253)
at android.view.View.getDisplayList(View.java:14315)
at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:273)
at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:279)
at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:318)
at android.view.ViewRootImpl.draw(ViewRootImpl.java:2561)
at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2377)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2007)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1086)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6453)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:846)
at android.view.Choreographer.doCallbacks(Choreographer.java:647)
at android.view.Choreographer.doFrame(Choreographer.java:601)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:829)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5254)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:927)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:713)

有人知道 Android 代码中是否有相关的 bug 吗?

27999 次浏览

问题在于 ViewGroupdispatchDraw()方法。此方法试图绘制 ViewGroup的所有子级。当一个孩子是 null时,你会得到一个异常,它很可能来自于 这条线: if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {(注意 mViewFlags)。

所以问题在于你的某个视图没有被正确地初始化。恐怕我只能做到这样了。

可能的解决方案

我也有同样的问题。我设置了一个 animation和在 onAnimationEnd我正在删除的对象,已被动画,这是当问题开始。我所做的就是设置一个 异步的 Runnable,在动画停止100毫秒后移除动画对象:

之前动画的对象是 this._loader

private void removeLoader() {
final ContentContainer self = this; // "CustomContainer" needs to match the type of `this`
Handler h = new Handler();
h.postAtTime(new Runnable() {
@Override
public void run() {
MainActivity.instance.runOnUiThread(new Runnable() {
@Override
public void run() {
try {
if(self._loader == null) {
// there is no loader. quit now while you still have the chance!!
return;
}
while(self._loader.getParent() != null) {
removeView(self._loader);
}
} catch(Exception e) {
Crashlytics.logException(e);
e.printStackTrace();
}


self._loader = null;
}
});
}
}, 100);
}

干杯

可能的原因: 我也有同样的问题。事实证明,当我添加代码来修改 onDraw ()调用中的视图树时,这种情况就开始发生了。具体来说,当某些条件得到满足时,我在派生的 onDraw ()中删除了带有子元素的视图。我现在认为这是一件坏事,可能是因为平台试图绘制我现在已经从视图树中删除的视图。我解决这个问题的方法是,在调用 onDraw ()完成之后,使用 Runnable 发布删除操作。

我们也开始意外地得到这个错误。问题出在动画片段上。更具体地说,当应用程序针对 Local Maven repository for Support Libraries rev > 26构建时,在片段事务中使用具有 replace()的自定义动画。

可能的解决办法

降低 Local Maven repository for Support Libraries到转速26,参见 给你

我也面临着同样的问题,我和 Handler 解决了。

new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
// remove fragment from here
}
});

方法,并将 try/catch 块放入其中,如下所示:

public void dispatchDraw(Canvas c)
{
try
{
super.dispatchDraw(c);
return;


}
catch(Exception exception)
{
return;
}
}

虽然这是丑陋的和不好的做法,我唯一可以得到可靠的工作,只是捕捉异常的 dispatchDraw()如下:

override fun dispatchDraw(canvas: Canvas?) {
/*
* We're doing this because of the below exception that is out of our control:
* java.lang.NullPointerException: Attempt to read from field
* 'int android.view.View.mViewFlags' on a null object reference at
* android.view.ViewGroup.dispatchDraw(ViewGroup.java:4111)
*/
try {
super.dispatchDraw(canvas)
} catch (e: NullPointerException) {


}
}

只要确保你所期望的行为正常工作,并且你不会因为这样做而破坏其他东西。 再说一次,不是很理想,但是这是我唯一能做的事情,而且我非常确定在我的情况下,它不会破坏任何东西。

祝你平安

我试图从 片段 A导航到 碎片 B,而 片段 A是一个需要从 碎片 B获取数据的表单。

因此,当我试图在不填充 A的情况下导航时,它抛出了这个异常。

而且,即使 A独立于数据 B,它也会抛出这个异常。

我不知道为什么,但我添加了一个条件,用户必须填写表单之前导航离开,这解决了问题。

这是一个线程问题。可能是您正在刷新您的 ViewPager或其他适配器。

已经断断续续地面对这个问题,并且意识到如果你把它放在你的 Activity的 UI 线程中,那么它就会呈现得很好。

activity?.runOnUiThread{
// Add Your UI Updating Methods Here
}

没有延迟/线程需要的东西。请阅读通过..。 这篇文章已经过时了,但是对于未来的读者来说,它将是有帮助的。 我详细解释了在我面对和解决的用例中真正发生的事情。

我有一个取景器,通过使用简单的滑入/滑出动画,通过它的孩子动画翻转下一个和上一个。我需要删除一个视图从取景器刚刚翻转完成,并确保它发生在正确的时间,幻灯片动画有一个监听器,我删除在那里的意见。一切都很顺利 经过一番反复试验,结果发现:

翻到下一页的顺序如下:

1-slide-out starts
2-slide-in starts
3-slide-out finishes
4-slide-in finishes

因此,在幻灯片上有监听器是正确的,因为这两个动画都保证在那里完成。

但是翻到前面是这样的顺序:

1-slide-in starts
2-slide-out starts
3-slide-in finishes
4-slide-out finishes

正如您所看到的,当滑入完成时,滑出还没有完成,因此会发生异常。我只是让听众滑出来,它工作得很完美。

虽然所有其他的回答都敦促删除视图与延迟(这当然有助于确保并发动画都完成了) ,但正如您可以看到的,情况并非如此。

一般来说,当一组动画在一个视图上运行时(即使持续时间完全相同)——尽管一般认为它们会一起完成——但它们不会(记住线程实际上是并发的)。

这主要是因为您在 touch Event 分派期间删除了一个视图。 在大型代码库中很难找到删除视图的用户。

对于那些想要进行崩溃分析的人,可以看看这篇文章:

Https://medium.com/@wanxiao1994/crash-analysis-resetcancelnextupflag-nullpointerexception-e409b7bdbad