Android ViewGroup崩溃。试图从空对象引用的字段'int android.view.View.mViewFlags'中读取数据

73 人关注

我们已经发现了几个由后端日志监控报告的这种崩溃的案例。似乎这些崩溃与特定的用户体验故障无关。而且从报告中,没有迹象表明我们自己的类是如何被卷入的(没有任何我们的类名的迹象)。下面是一个典型崩溃的例子。

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代码的相关错误记录?

3 个评论
我想补充的是,我在用SwipeRefreshLayout和空的RecyclerView刷新时遇到过这种情况。
@MathieuMaree 你是否检查过 github.com/worker8/TourGuide/pull/34 链接?作者似乎在他的案例中找到了一个解决方法--谁知道呢,也许它也会帮助你。
gio
请提供可用于重现该崩溃的代码。
java
android
viewgroup
M2014
M2014
发布于 2015-10-21
11 个回答
Jacksonkr
Jacksonkr
发布于 2022-11-01
已采纳
0 人赞同

Possible Solution

我也有这个问题。我设置了一个 animation ,在 onAnimationEnd 中我正在删除已经被动画化的对象,这就是问题开始的时候。我所做的是设置一个 非同步的 替换代码2】,在动画停止后等待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);

Cheers

@DeanWild It's a total hack, I agree. I'm sure there is a tread-safe / correct way in doing this but I dare say the time & creativity needed for a "proper" solution outweighs the benefits of a quick hack, but I've been wrong before...
@Jacksonkr 谢谢你的回答。在我的案例中,只要把 removeView 的调用放在一个 runnable 中,而这个 post 是在 removeView 被调用的同一个视图上,就可以解决这个问题。这感觉是一个 "更合适 "的解决方案,因为不涉及延迟。
你是否跳过了 runOnUiThread ?根据不同的设置,这可能会让一个人陷入困境。
通过使用 postAtTime() ,你无意中做的是永远不会调用runnable。我相信你的意思是 postDelayed() ,但是这解决了你的问题,让我相信这根本就不是一个解决方案;你实际上是把视图保留在内存中。这对于诸如在RecyclerView中为视图制作动画的情况来说,真的不是一个解决方案。
Pawan Chaurasiya
Pawan Chaurasiya
发布于 2022-11-01
0 人赞同

我也面临同样的问题。我用处理程序解决了这个问题。

new Handler(Looper.getMainLooper()).post(new Runnable() {
                @Override
                public void run() {
                   // remove fragment from here
    
从错误信息中很不明显,但在我的例子中,它是在AnimationListener中删除了视图。发布它回到主线程就解决了问题。
kevinpelgrims
kevinpelgrims
发布于 2022-11-01
0 人赞同

问题出在 ViewGroup dispatchDraw() 方法中。这个方法试图绘制所有 ViewGroup 的子代。当一个孩子被 null 时,你会得到一个异常,这个异常很可能来自于 this line : if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { (notice the mViewFlags ).

所以问题在于你的某个视图,在某个地方没有被正确初始化。恐怕这就是我所能做的最好的了。

veritas1
veritas1
发布于 2022-11-01
0 人赞同

We started getting this error unexpectedly too. It was tracked down to fragment animations being the issue. More specifically using custom animations with replace() in a fragment transaction when the app is built against Local Maven repository for Support Libraries rev > 26.

Possible solution

Local Maven repository for Support Libraries 降级为第26版。 见 here

Teemu Lätti
Teemu Lätti
发布于 2022-11-01
0 人赞同

可能的原因。 我遇到了完全相同的问题。事实证明,当我在onDraw()调用中加入修改视图树的代码时,它开始发生了。具体来说,当某些条件得到满足时,我在派生的onDraw()中删除了一个带孩子的视图。我现在相信这是一件坏事,可能是因为平台正试图绘制我现在已经从视图树中删除的视图。我通过在调用onDraw()结束后用Runnable发布删除信息来解决这个问题。

Pierpaolo Pierpaoli
Pierpaolo Pierpaoli
发布于 2022-11-01
0 人赞同

覆盖dispatchDraw方法,并在其中放入一个try/catch块,像这样。

public void dispatchDraw(Canvas c)
            super.dispatchDraw(c);
            return;
        catch(Exception exception)
            return;
    
Bassam Helal
Bassam Helal
发布于 2022-11-01
0 人赞同

虽然这很难看,也不是很好的做法,但我唯一能做到的就是像下面这样在 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) {

只要确保你所期望的行为是正确的,你这样做不会破坏其他东西。 同样,这并不理想,但这是我唯一能做到的,而且我绝对相信在我的情况下,它不会破坏任何其他东西。

祝你平安 :)

Anirudh Ganesh
Anirudh Ganesh
发布于 2022-11-01
0 人赞同

我正试图从 片段A to 片段B ,以及 片段A 是一个表格,需要从 片段B .

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

另外,即使 A 是独立于数据的 B ,它被抛出了这个异常。

我不知道为什么,但我添加了一个条件,用户必须在导航前填好表格,这就解决了问题。

Morgan Koh
Morgan Koh
发布于 2022-11-01
0 人赞同

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

我一直在面对这个问题,并意识到,如果你把它放在你的 Activity 的UI线程中,那么它就会呈现得很好。

activity?.runOnUiThread{
   // Add Your UI Updating Methods Here
    
Masoud Dadashi
Masoud Dadashi
发布于 2022-11-01
0 人赞同

不需要延迟/线程的东西。请仔细阅读... 这篇文章是旧的,但对于未来的读者来说,这将是有帮助的。 我详细解释了我所面临和解决的一个用例的真实情况。

我有一个viewflipper,它使用简单的滑入/滑出动画在其子代中进行下一个和上一个的翻转。我需要在翻转完成后从viewflipper中移除一个视图,为了确保它在正确的时间发生,滑入动画有一个监听器,我在那里移除视图。对于翻转到下一个,一切都工作得很完美 而翻转到上一个则出现了上述的异常。经过一些试验和错误,结果发现。

when flipping to next the following is the sequence:

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

因此,在滑轨上设置听众是正确的,因为两个动画都能保证在那里完成。

But when flipping to previous the following is the sequence:

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

正如你所看到的,当slide-in完成时,slide-out还没有完成,因此出现了异常。我简单地将监听器设置在滑出时,它就完美地工作了。

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

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