Подтвердить что ты не робот

Android 4.4 Случайная авария KitKat

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

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

Мы видели крах на Nexus 4 и 5, как с Android 4.4 KitKat.

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

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

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

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

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

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(), содержит фреймы, иначе он довольно стандартный и простой.

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

4b9b3361

Ответ 1

Я также столкнулся с этой проблемой с некоторым кодом, который я поддерживал. Я смог последовательно реплицировать ошибку, включив 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.isShown() следующим образом:

public boolean isShown(){
  return someCondition;
}

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

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

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

public boolean isShown(){
  return super.isShown() && someCondition;
}

Ответ 2

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

Диагноз

Взгляните на этот бит метода KitKat View#setFlags() на 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(). В моем приложении это одно место является единственным крахом, и это происходит при запуске приложения. Это не приятная идея, но одна из возможностей заключается в том, чтобы переупорядочить вещи, чтобы раздуть это мнение позже.