Android 4.4 KitKat случайный сбой

РЕДАКТИРОВАТЬ: Перед тем, как проголосовать против и намекать на вещи, пожалуйста, поймите, что я не могу воспроизвести эту ошибку. Это происходит постоянно на определенных устройствах, к которым у меня нет доступа, но не после сброса прошивки!

Недавно я обнаружил случайные сбои в приложении, которое разрабатываю для клиента. Через 3 года приложение насчитывает около 100 000 активных пользователей.

Мы видели сбой на Nexus 4 и 5, оба с Android 4.4 KitKat.

Мы не можем воспроизвести это на наших собственных Nexus 4 и 5 под управлением 4.4.

У нас есть заказчик через нашу службу поддержки. Он сказал нам, что сбой происходит каждый раз в одном и том же месте при вызове нового действия. Он управлял Dalvik, а не ART. После сброса прошивки приложение работало нормально и не могло воспроизвести его снова!

Я не могу опубликовать исходный код или макет по юридическим причинам, но у меня есть эта трассировка стека:

java.lang.RuntimeException: Unable to start activity ComponentInfo{xx.xxx.xxxxx.xxx.xxxxxx.prod/xx.xxx.xxxxx.xxx.PaymentsActivity}: java.lang.NullPointerException
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2176)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2226)
at android.app.ActivityThread.access$700(ActivityThread.java:135)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1397)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4998)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:777)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:593)
at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:126)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.NullPointerException
at android.view.View.sendAccessibilityEventUncheckedInternal(View.java:4938)
at android.view.View.sendAccessibilityEventUnchecked(View.java:4919)
at android.view.View$SendViewStateChangedAccessibilityEvent.run(View.java:19433)
at android.view.View$SendViewStateChangedAccessibilityEvent.runOrPost(View.java:19465)
at android.view.View.notifyViewAccessibilityStateChangedIfNeeded(View.java:7265)
at android.view.View.setFlags(View.java:8990)
at android.view.View.setVisibility(View.java:6020)
at android.view.LayoutInflater.parseInclude(LayoutInflater.java:859)
at de.robv.android.xposed.XposedBridge.invokeOriginalMethodNative(Native Method)
at de.robv.android.xposed.XposedBridge.handleHookedMethod(XposedBridge.java:547)
at android.view.LayoutInflater.parseInclude(Native Method)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:745)
at android.view.LayoutInflater.inflate(LayoutInflater.java:492)
at de.robv.android.xposed.XposedBridge.invokeOriginalMethodNative(Native Method)
at de.robv.android.xposed.XposedBridge.handleHookedMethod(XposedBridge.java:547)
at android.view.LayoutInflater.inflate(Native Method)
at android.view.LayoutInflater.inflate(LayoutInflater.java:397)
at android.view.LayoutInflater.inflate(LayoutInflater.java:353)
at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:290)
at android.app.Activity.setContentView(Activity.java:1928)
at xx.xxx.xxxxx.xxx.StandardActivity.setContentView(StandardActivity.java:289)
at xx.xxx.xxxxx.xxx.PaymentsActivity.onCreate(PaymentsActivity.java:61)
at android.app.Activity.performCreate(Activity.java:5243)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2140)
... 12 more

РЕДАКТИРОВАТЬ: вторая трассировка стека без xposed

java.lang.RuntimeException: Unable to start activity ComponentInfo{xx.xxx.xxxxx.xxx.xxxxx.prod/xx.xxx.xxxxx.xxx.PaymentsActivity}: java.lang.NullPointerException
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2176)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2226)
at android.app.ActivityThread.access$700(ActivityThread.java:135)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1397)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4998)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:777)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:593)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.NullPointerException
at android.view.View.sendAccessibilityEventUncheckedInternal(View.java:4938)
at android.view.View.sendAccessibilityEventUnchecked(View.java:4919)
at android.view.View$SendViewStateChangedAccessibilityEvent.run(View.java:19433)
at android.view.View$SendViewStateChangedAccessibilityEvent.runOrPost(View.java:19465)
at android.view.View.notifyViewAccessibilityStateChangedIfNeeded(View.java:7265)
at android.view.View.setFlags(View.java:8990)
at android.view.View.setVisibility(View.java:6020)
at android.view.LayoutInflater.parseInclude(LayoutInflater.java:859)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:745)
at android.view.LayoutInflater.inflate(LayoutInflater.java:492)
at android.view.LayoutInflater.inflate(LayoutInflater.java:397)
at android.view.LayoutInflater.inflate(LayoutInflater.java:353)
at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:290)
at android.app.Activity.setContentView(Activity.java:1928)
at xx.xxx.xxxxx.xxx.StandardActivity.setContentView(StandardActivity.java:289)
at xx.xxx.xxxxx.xxx.PaymentsActivity.onCreate(PaymentsActivity.java:61)
at android.app.Activity.performCreate(Activity.java:5243)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2140)
... 11 more

Макет, устанавливаемый в setContentView (), содержит фреймы, иначе он довольно стандартный и простой.

Любой ввод приветствуется :-)


person Jens Vesti    schedule 06.12.2013    source источник
comment
Проблема может быть в макете, возможно, вам поможет эта страница . Проверить, изменилось ли что-то, что вы использовали в 4.4 ... без кода немного сложно.   -  person Marco Acierno    schedule 06.12.2013
comment
NPE не должно быть сложно найти и решить   -  person Robin Dijkhof    schedule 06.12.2013
comment
NPE в коде фреймворка, подобном этому, может быть труднее найти и решить, поскольку у вас нет прямого контроля над этим кодом. Хотя вы можете получить исходный код из AOSP, чтобы отследить поток кода и лучше понять, почему может происходить NPE.   -  person JesusFreke    schedule 07.12.2013
comment
Кроме того, я не уверен, почему по этому вопросу были высказаны отрицательные голоса. Мне это кажется вполне законным вопросом.   -  person JesusFreke    schedule 07.12.2013
comment
Запись de.robv.android.xposed.XposedBridge.main (XposedBridge.java:126) в трассировке стека выглядит несколько подозрительно. xposed - это фреймворк для подключения и возможного изменения поведения классов. (repo.xposed.info/module/de.robv.android.xposed .installer)   -  person JesusFreke    schedule 07.12.2013
comment
Не уверен, почему это было отклонено. Позвольте пояснить, я не могу воспроизвести ошибку @Robin Dijkhof. NPE находится глубоко в структуре Android.   -  person Jens Vesti    schedule 07.12.2013
comment
Я очень подозреваю, что это может быть связано с использованием репортерами фреймворка xposed.   -  person JesusFreke    schedule 07.12.2013
comment
Я не использую xposed, @JesusFreke - фреймворк Android. Все, что указано выше «в android.app.Activity.setContentView (Activity.java:1928)», находится вне моего контроля.   -  person Jens Vesti    schedule 07.12.2013
comment
@JensVesti Я думаю, они говорят о человеке, сообщившем о проблеме, а не о вашем приложении.   -  person hichris123    schedule 07.12.2013
comment
Верный. Похоже, что человек, сообщивший об ошибке, по какой-то причине использует фреймворк xposed, и вполне возможно, что он может делать что-то, что в конечном итоге приводит к сбою, о котором было сообщено. Другими словами, это может быть вообще не ошибка в вашем приложении.   -  person JesusFreke    schedule 07.12.2013
comment
Ах, спасибо @ hichris123 :) Я обновлю другую трассировку стека, которая не содержит xposed.   -  person Jens Vesti    schedule 07.12.2013
comment
Ах хорошо. Если вы видели ошибку в другом месте (то есть без записей xposed в трассировке стека), то это вряд ли проблема. В этом случае я рекомендую взять исходный код AOSP и посмотреть на место, указанное в трассировке стека. Оттуда вы можете работать в обратном направлении, пытаясь определить, какие условия могут вызвать NPE в этот момент.   -  person JesusFreke    schedule 07.12.2013
comment
Хорошая идея, @JesusFreke! И спасибо за ссылку, @Marco Lopez Acierno - я действительно должен проверить компилятор, действительно компилирующий код. Это сервер сборки, над которым я не контролирую, возможно, на нем не установлена ​​последняя версия компилятора.   -  person Jens Vesti    schedule 07.12.2013


Ответы (2)


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

Во-первых, вот метод из View.java, в котором использовалась пустая ссылка, вызвавшая сбой, из выпуска KitKat для Android:

void sendAccessibilityEventUncheckedInternal(AccessibilityEvent event) {
    if (!isShown()) {
        return;
    }
    onInitializeAccessibilityEvent(event);
    // Only a subset of accessibility events populates text content.
    if ((event.getEventType() & POPULATING_ACCESSIBILITY_EVENT_TYPES) != 0) {
        dispatchPopulateAccessibilityEvent(event);
    }
    // In the beginning we called #isShown(), so we know that getParent() is not null.
    getParent().requestSendAccessibilityEvent(this, event);
}

Для меня основной причиной оказался пользовательский View, который переопределил View.isShown () следующим образом:

public boolean isShown(){
  return someCondition;
}

Это означало, что sendAccessibilityEventUncheckedInternal будет проходить мимо проверки if (! IsShown ()), которую он выполняет перед продолжением, даже если у View был нулевой родительский элемент, что вызвало сбой.

Первоначально я думал, что это проблема параллелизма, потому что я предполагал, что проверка isShown () гарантирует, что родительский элемент не равен нулю и что ссылка на родительский элемент View была изменена во время выполнения sendAccessibilityEventUncheckedInternal. Неправильный!

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

public boolean isShown(){
  return super.isShown() && someCondition;
}
person Captain Blammo    schedule 02.01.2014
comment
Вы молодец, @Captain Blammo! На самом деле было переопределение isShown (), я все еще не могу его воспроизвести, но применим предложенное вами исправление, чтобы увидеть, исправляет ли оно это :-) - person Jens Vesti; 10.01.2014
comment
Это решило аналогичную проблему, поскольку я тоже создал собственный View и переопределил isShown()-метод. - person deubaka; 25.03.2014
comment
Этот ответ меня спас ... видимо, у меня была такая же проблема. Мое приложение отлично работало на устройстве Samsung и вылетало на устройстве LGG3! Однако это исправление решило мою проблему. Спасибо! - person Cookienator; 22.10.2015
comment
Выполнено! Я никогда больше не слышал и не видел ничего, связанного с этой проблемой, так что, должно быть, это сработало :) - person Jens Vesti; 12.11.2015
comment
Спасибо чувак. Удачного кодирования! - person Captain Blammo; 12.11.2015

Мои пользователи сталкивались с той же проблемой, и, похоже, она была вызвана включением одного или нескольких параметров доступности. Некоторые из моих пользователей использовали умные часы Pebble, которые устанавливали опцию специальных возможностей - так что это не просто TalkBack и т. Д.

Диагноз

Взгляните на этот фрагмент метода View#setFlags() KitKat по адресу https://github.com/android/platform_frameworks_base/blob/kitkat-mr1-release/core/java/android/view/View.java#L9006

if (accessibilityEnabled) {
  ...
  notifyViewAccessibilityStateChangedIfNeeded(
                        AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}

который отправляет вас в кроличью нору, оканчивающуюся на NullPointerException , если он выполняется до того, как представление будет прикреплено к иерархии представлений (т. е. не имеет родителя), потому что в View#sendAccessibilityEventUncheckedInternal() по адресу https://github.com/android/platform_frameworks_base/blob/kitkat-mr1-release/core/java/android/view/View.java#L4952 имеем:

getParent().requestSendAccessibilityEvent(this, event);

Мое обходное решение (похоже, оно вам не подходит)

Для своего приложения я программно создаю подкласс View и вызываю View#setOnClickListener() в конструкторе. Вместо этого я сейчас звоню View#setOnClickListener() из

@Override
protected void onAttachedToWindow() {
    super.onAttachedToWindow();
    /* Due to a bug in how Android 4.4 handles accessibility options,
     * we can't set the onClick listener until this View has a parent or we will
     * get an NPE. */
    setOnClickListener(this);
}

Это работает, потому что у этого View будет родитель к моменту вызова View#onAttachedToWindow().

Однако ваша трассировка стека более проблематична. Вы попадаете в кроличью нору через атрибуты в макете XML. Я не придумал для вас идеи. Одна мысль состоит в том, что это должно происходить только при запуске приложения - иначе практически все инфляции макетов XML вызовут сбой, потому что существует очень много путей, которые проведут вас через View#setFlags(). В моем приложении это единственное место, по-видимому, единственное сбой, происходящее при запуске приложения. Это неприятная идея, но одна из возможностей - переупорядочить вещи, чтобы позже раздуть эту точку зрения.

person bpenrod    schedule 17.12.2013
comment
Молодец, @bpenrod! Я предполагаю, что это тоже проблема параллелизма: void sendAccessibilityEventUncheckedInternal(AccessibilityEvent event) { if (!isShown()) { //<-- parent is not null return; } ... // In the beginning we called #isShown(), so we know that getParent() is not null. getParent().requestSendAccessibilityEvent(this, event); //<-- parent is now null, same thread :-| } (РЕДАКТИРОВАТЬ: ^ Я понятия не имею, как правильно отформатировать этот код) Или кто-то в пути вызова установил для него значение null. Мне нужно еще покопать. - person Jens Vesti; 18.12.2013
comment