Сбой Android ViewGroup: попытка чтения из поля «int android.view.View.mViewFlags» по нулевой ссылке на объект

Мы обнаружили несколько случаев такого рода сбоев, о которых сообщает мониторинг ведения журнала серверной части. Похоже, сбои не связаны с конкретным сбоем 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?


person M2014    schedule 20.10.2015    source источник
comment
Я вижу точно такой же сбой, но я не видел, чтобы он упоминался где-либо еще. Пока это происходит только на устройствах Marshmallow, по крайней мере, для меня.   -  person sa.shadow    schedule 04.11.2015
comment
Я столкнулся с той же проблемой. В отличие от @sa.shadow, я также сталкивался с ним на устройствах до Marshmallow, особенно на Lollipop. Еще я заметил, что это происходит только тогда, когда мой список (RecyclerView) пуст.   -  person MathieuMaree    schedule 11.01.2016
comment
Я хочу добавить, что я сталкивался с этим при пролистывании для обновления с помощью SwipeRefreshLayout и пустого RecyclerView.   -  person MathieuMaree    schedule 11.01.2016
comment
Бьюсь об заклад, это ошибка Android :) Можете ли вы поделиться кодом на git для его отладки?   -  person ceph3us    schedule 12.01.2016
comment
Я получаю это при анимации просмотра карточек на новых устройствах Nexus. Никаких проблем с устройством Samsung, которое у меня есть.   -  person Jimeux    schedule 12.01.2016
comment
@MathieuMaree Вы проверили ссылку github.com/worker8/TourGuide/pull/34? Похоже автор нашел обходной путь в своем случае - кто знает, может и вам поможет   -  person Konstantin Loginov    schedule 12.01.2016
comment
пожалуйста, предоставьте код, который можно использовать для воспроизведения этого сбоя   -  person gio    schedule 16.01.2016


Ответы (11)


Возможное решение

У меня была такая же проблема. Я настроил 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);
}

Ура!

person Jacksonkr    schedule 04.02.2016
comment
фу, ненавижу делать такие вещи, в любом случае ура за хедз-ап - person Dean Wild; 11.03.2016
comment
@DeanWild Это полный взлом, я согласен. Я уверен, что есть безопасный/правильный способ сделать это, но я осмелюсь сказать, что время и творческий подход, необходимые для правильного решения, перевешивают преимущества быстрого взлома, но я ошибался раньше... - person Jacksonkr; 11.03.2016
comment
@Jacksonkr Спасибо за ответ. В моем случае просто поместите вызов removeView в runnable, который posted в том же представлении, что и removeView, исправив проблему. Это похоже на более правильное решение, поскольку здесь нет задержки. - person Daniel F; 08.03.2017
comment
Вы пропускаете runOnUiThread ? В зависимости от установки, которая может попасть в горячую воду. - person Jacksonkr; 08.03.2017
comment
Была такая же проблема с вызовом removeView в основном потоке. Решение @DanielF сработало для меня. Создание обработчика postDelayed сработало. - person jds17; 15.06.2017
comment
плюс 1 за предположение, что это может быть связано с анимацией. - person Priya Singhal; 04.10.2017
comment
Используя postAtTime() то, что вы делаете непреднамеренно, никогда не вызывает runnable. Я уверен, что вместо этого вы имели в виду postDelayed(), но тот факт, что это решило вашу проблему, заставляет меня поверить, что это вовсе не решение; вы эффективно сохраняете представления в памяти. Это действительно не решение для таких случаев использования, как анимация представлений в RecyclerView. - person Paul Lammertsma; 22.10.2018

Я столкнулся с той же проблемой. Я решил с Handler.

new Handler(Looper.getMainLooper()).post(new Runnable() {
                @Override
                public void run() {
                   // remove fragment from here
                }
            });
person Pawan Chaurasiya    schedule 13.05.2016
comment
Очень не очевидно из сообщения об ошибке, но в моем случае это было удаление представления в AnimationListener. размещение его, чтобы вернуться к основной теме, решило проблему. - person David Berry; 31.03.2020

Проблема в методе ViewGroup dispatchDraw(). Этот метод пытается отрисовать всех дочерних элементов ViewGroup. Когда ребенку null, вы получаете исключение, которое, скорее всего, исходит от эта строка: if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { (обратите внимание на mViewFlags).

Итак, проблема в том, что одно из ваших представлений где-то неправильно инициализировано. Боюсь, это лучшее, что я могу сделать.

person kevinpelgrims    schedule 16.01.2016

Мы тоже неожиданно начали получать эту ошибку. Было обнаружено, что проблема заключается в анимации фрагментов. В частности, использование пользовательских анимаций с replace() в транзакции фрагмента, когда приложение создано для Local Maven repository for Support Libraries rev > 26.

Возможное решение

Понизьте версию Local Maven repository for Support Libraries до версии 26. См. здесь.

person veritas1    schedule 30.03.2016

Возможная причина: у меня была точно такая же проблема. Оказалось, что это начало происходить, когда я добавил код для изменения дерева представления в вызове onDraw(). Чтобы быть конкретным, я удалил представление с дочерними элементами в моем производном методе onDraw(), когда были выполнены определенные условия. Теперь я считаю, что это плохо, возможно, потому, что платформа пытается рисовать представления, которые я сейчас удалил из дерева представлений. Я решил проблему, опубликовав удаление с помощью Runnable после завершения вызова onDraw().

person Teemu Lätti    schedule 30.03.2016

Переопределите метод dispatchDraw и поместите в него блок try/catch, например:

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

        }
        catch(Exception exception)
        {
            return;
        }
    }
person Pierpaolo Pierpaoli    schedule 08.12.2017

Несмотря на то, что исключение является распространенным, источник, вызвавший его, необычен и возникает, когда у вас так много динамических представлений. Иногда это исключение вызывает прокрутка на странице Instagram. Однако, если разобраться в этом, проблема с оборудованием может стать проблемой. Так что просто обработайте (поймать) проблему.

person Pradeep    schedule 11.06.2019

Хотя это уродливо и не очень хорошо, единственное, что я могу заставить надежно работать, — это просто перехватить исключение в 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) {

        }
    }

Просто убедитесь, что желаемое поведение работает правильно, и вы не нарушаете что-то еще, делая это. Опять же, не идеально, но это единственное, что я мог заставить работать, и я абсолютно уверен, что в моем случае это ничего не сломает.

Мир тебе :)

person Bassam Helal    schedule 22.03.2020

Я пытался перейти от фрагмента A к фрагменту B, а фрагмент A был формой, требующей данных из фрагмента B.

Поэтому, когда я пытался перемещаться, не заполняя A, возникало это исключение.

Кроме того, даже если A не зависит от данных B, возникает это исключение.

Я понятия не имею, почему, но я добавил условие, при котором пользователь должен был заполнить форму, прежде чем уйти, и это решило проблему.

person Anirudh Ganesh    schedule 01.06.2020

Это проблема с потоками. Возможно, вы обновляете свой ViewPager или какой-либо другой адаптер.

Сталкивались с этой проблемой время от времени и поняли, что если вы разместите ее в потоке пользовательского интерфейса в своем Activity, то она будет отображаться очень хорошо.

activity?.runOnUiThread{
   // Add Your UI Updating Methods Here
}
person Morgan Koh    schedule 25.08.2020

Никаких задержек/потоков не требуется. Пожалуйста, прочитайте... Пост старый, но будущим читателям он будет полезен. Я подробно объясняю, что на самом деле происходит с вариантом использования, с которым я столкнулся и решил.

У меня есть 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

Как видите, когда вставка заканчивается, выскальзывание еще не сделано, и, следовательно, возникает исключение. Я просто установил слушателя на слайд-аут, и он работал отлично.

В то время как все другие ответы призывают удалить представление с задержкой (что, конечно, помогает убедиться, что все параллельные анимации выполнены), но, как вы могли видеть, это не так.

Как правило, когда у вас есть набор анимаций, работающих в представлении (даже с одинаковой продолжительностью), хотя общее мнение состоит в том, что они завершатся вместе, но это не так (помните, что потоки практически параллельны) .

person Masoud Dadashi    schedule 22.04.2021
comment
Спасибо за ваш вклад. Я отошел от кода Android Java на несколько лет. Однако ваш ответ выглядит как хорошее объяснение причины. - person M2014; 23.04.2021