Android ViewGroup 崩溃:尝试从空对象引用上的字段“int android.view.View.mViewFlags”读取
Posted
技术标签:
【中文标题】Android ViewGroup 崩溃:尝试从空对象引用上的字段“int android.view.View.mViewFlags”读取【英文标题】:Android ViewGroup crash: Attempt to read from field 'int android.view.View.mViewFlags' on a null object reference 【发布时间】:2016-01-19 11:22:45 【问题描述】:我们发现了几个后端日志监控报告的此类崩溃案例。崩溃似乎与特定的 UX 故障无关。而且从报告中看,没有迹象表明我们自己的课程是如何参与的(没有任何我们的课程名称的迹象)。以下是典型崩溃的示例:
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 代码的相关错误记录?
【问题讨论】:
我想补充一点,在使用 SwipeRefreshLayout 和空的 RecyclerView 刷卡刷新时,我一直在体验它 @MathieuMaree 你检查过github.com/worker8/TourGuide/pull/34 链接吗?似乎作者在他的案例中找到了解决方法 - 谁知道呢,也许它也会对您有所帮助 请提供可用于重现此崩溃的代码 【参考方案1】:可能的解决方案
我也有同样的问题。我设置了一个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);
干杯
【讨论】:
@DeanWild 我同意,这完全是骇客。我确信有一种安全/正确的方法可以做到这一点,但我敢说“正确”解决方案所需的时间和创造力超过了快速破解的好处,但我之前错了...... @Jacksonkr 感谢您的回答。在我的情况下,只需将removeView
调用放在runnable
中,即post
ed 在同一视图上调用removeView
即可解决问题。这感觉像是一个“更合适”的解决方案,因为不涉及任何延迟。
您要跳过runOnUiThread
吗?取决于可能使人陷入热水的设置。
通过使用postAtTime()
你无意中做的事情,永远不会调用runnable。我确定您的意思是postDelayed()
,但是这解决了您的问题这一事实使我相信这根本不是解决方案;您有效地将视图保存在内存中。对于 RecyclerView 中的动画视图等用例来说,这确实不是一个解决方案。【参考方案2】:
我遇到了同样的问题。我用 Handler 解决了。
new Handler(Looper.getMainLooper()).post(new Runnable()
@Override
public void run()
// remove fragment from here
);
【讨论】:
从错误消息中非常不明显,但在我的情况下,它正在删除 AnimationListener 中的视图。发布它以返回主线程解决了问题。【参考方案3】:问题出在ViewGroup
的dispatchDraw()
方法中。此方法尝试绘制所有ViewGroup
的孩子。当一个孩子是null
时,你会得到一个异常,它很可能来自this line:if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE)
(注意mViewFlags
)。
所以问题是您的某个视图没有正确初始化。恐怕这是我能做的最好的了。
【讨论】:
【参考方案4】:我们也开始意外地收到此错误。它被追踪到片段动画是问题所在。更具体地说,当针对 Local Maven repository for Support Libraries
rev > 26 构建应用时,在片段事务中使用带有 replace()
的自定义动画。
可能的解决方案
将Local Maven repository for Support Libraries
降级到第 26 版。请参阅here
【讨论】:
【参考方案5】:可能的原因: 我遇到了完全相同的问题。事实证明,当我在 onDraw() 调用中添加代码来修改视图树时,它就开始发生了。具体来说,当满足某些条件时,我在派生的 onDraw() 中删除了一个带有子视图的视图。我现在认为这是一件坏事,可能是因为平台正在尝试绘制我现在从视图树中删除的视图。我通过在对 onDraw() 的调用完成后使用 Runnable 发布删除来解决了这个问题。
【讨论】:
【参考方案6】:重写 dispatchDraw 方法并在其中放入一个 try/catch 块,如下所示:
public void dispatchDraw(Canvas c)
try
super.dispatchDraw(c);
return;
catch(Exception exception)
return;
【讨论】:
【参考方案7】:虽然这很丑陋而且不是好的做法,但我唯一能可靠地工作的就是在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)
只需确保您想要的行为正常工作,并且这样做不会破坏其他东西。 同样,不理想,但这是我唯一可以开始工作的事情,而且我绝对确定在我的情况下它不会破坏其他任何东西。
愿你平安:)
【讨论】:
【参考方案8】:我试图从 Fragment A 导航到 Fragment B,而 Fragment A 是一个需要来自 Fragment 的数据的表单B。
所以当我尝试在不填写 A 的情况下进行导航时,它抛出了这个异常。
此外,即使 A 独立于数据 B,它也会抛出此异常。
我不知道为什么,但我添加了一个条件,即用户必须在离开之前填写表格,这解决了问题。
【讨论】:
【参考方案9】:这是一个线程问题。可能是您正在刷新您的 ViewPager
或其他适配器。
一直在面对这个问题,并意识到如果将它放在Activity
的 UI 线程中,那么它会呈现得很好。
activity?.runOnUiThread
// Add Your UI Updating Methods Here
【讨论】:
【参考方案10】:不需要延迟/线程。请通读... 这篇文章很旧,但对于未来的读者来说,它会有所帮助。 我详细解释了我遇到并解决的用例的实际情况。
我有一个 viewflipper,它使用简单的滑入/滑出动画在其子项中翻转下一个和上一个动画。我需要在翻转完成后从 viewflipper 中删除一个视图,并确保它在正确的时间发生,滑入式动画有一个侦听器,我可以在其中删除视图。一切都非常适合翻转到下一个 而翻转到上一个抛出了上述异常。经过反复试验,结果是:
当翻到下一个时,顺序如下:
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
如您所见,当滑入完成时,滑出尚未完成,因此会发生异常。我只是将监听器设置为滑出式,效果很好。
虽然所有其他答案都要求延迟删除视图(这当然有助于确保并发动画全部完成),但您可以看到情况并非如此。
作为一般规则,当您在视图上运行一组动画时(即使持续时间完全相同) - 尽管普遍认为它们会一起完成 - 但它们不会(请记住线程是几乎并发)。
【讨论】:
以上是关于Android ViewGroup 崩溃:尝试从空对象引用上的字段“int android.view.View.mViewFlags”读取的主要内容,如果未能解决你的问题,请参考以下文章
尝试从空对象引用上的字段“android.view.View androidx.recyclerview.widget.RecyclerView$b0.a”读取
Activity 的 Android ViewBinding 需要 ViewGroup