Android 4.4 KitKat random crash

后端 未结 2 409
自闭症患者
自闭症患者 2020-12-29 04:41

EDIT: Before down-voting and implying things, please understand I cannot reproduce this error. This happens constantly on certain devices which I do not have acces

相关标签:
2条回答
  • 2020-12-29 04:53

    My users have been running into the same problem and it appears to be caused by one or more accessibility options being turned on. Some of my users were using the Pebble smart watch which installs an accessibility option - so it's not just TalkBack, etc.

    The diagnosis

    Take a look at this bit of KitKat's View#setFlags() method at 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);
    }
    

    that sends you down the rabbit hole ending in a NullPointerException if it's executed before the View is attached to the view hierarchy (i.e. is has no parent) because in View#sendAccessibilityEventUncheckedInternal() at https://github.com/android/platform_frameworks_base/blob/kitkat-mr1-release/core/java/android/view/View.java#L4952 we have:

    getParent().requestSendAccessibilityEvent(this, event);
    

    My workaround (which looks like it won't work for you)

    For my app, I am creating a View subclass programmatically and was calling View#setOnClickListener() in the constructor. Instead, I now call View#setOnClickListener() from

    @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);
    }
    

    It works because this View will have a parent by the time View#onAttachedToWindow() gets called.

    Your stack trace is more problematic though. You're falling into the rabbit hole via attributes on an XML layout. I haven't come up with an idea for you. One thought is that this must only happen at app startup - otherwise virtually all inflations of XML layouts would trigger the crash because there are so many paths that take you through View#setFlags(). In my app, this one spot appears to be the only crash and it happens at app startup. It's not a pleasant idea but one possibility is to re-order things to inflate this view later.

    0 讨论(0)
  • 2020-12-29 04:59

    I've also run into this same problem with some code I was maintaining. I was able to consistently replicate the bug by enabling TalkBack in the accessibility options.

    First, here's the method from View.java where the null reference that caused the crash was used, from the KitKat release of 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);
    }
    

    For me, the root cause turned out to be a custom View which had overridden View.isShown() like so:

    public boolean isShown(){
      return someCondition;
    }
    

    This meant that sendAccessibilityEventUncheckedInternal would run past the if(!isShown()) check that it makes before proceeding even when the View had a null parent, and so caused the crash.

    I had originally thought it was a concurrency problem, because I assumed the isShown() check had ensured the parent wasn't null and that the reference to the View's parent had been changed during the execution of sendAccessibilityEventUncheckedInternal. Wrong!

    If you find a similar problem, especially in code you didn't write, you can prevent this crash pretty easily by including the result of the superclass's isShown() (assuming you are changing code in a direct subClass of View):

    public boolean isShown(){
      return super.isShown() && someCondition;
    }
    
    0 讨论(0)
提交回复
热议问题