【问题标题】:Android ViewGroup crash: Attempt to read from field 'int android.view.View.mViewFlags' on a null object referenceAndroid ViewGroup 崩溃:尝试从空对象引用上的字段“int android.view.View.mViewFlags”读取
【发布时间】: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 链接吗?似乎作者在他的案例中找到了解决方法 - 谁知道呢,也许它也会对您有所帮助
  • 请提供可用于重现此崩溃的代码

标签: java android viewgroup


【解决方案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 中,即posted 在同一视图上调用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】:

问题出在ViewGroupdispatchDraw() 方法中。此方法尝试绘制所有ViewGroup 的孩子。当一个孩子是null 时,你会得到一个异常,它很可能来自this lineif ((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
                

                如您所见,当滑入完成时,滑出尚未完成,因此会发生异常。我只是将监听器设置为滑出式,效果很好。

                虽然所有其他答案都要求延迟删除视图(这当然有助于确保并发动画全部完成),但您可以看到情况并非如此。

                作为一般规则,当您在视图上运行一组动画时(即使持续时间完全相同) - 尽管普遍认为它们会一起完成 - 但它们不会(请记住线程是几乎并发)。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2022-07-03
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多