IndexOutOfBoundsException on a textwatcher

The answer has already been given in a comment, but hard to see if you're just skimming by like me. So the real answer is:

Avoid adding the TextWatcher twice or more times, as the first one will edit the content and the subsequent ones will then cause an exception.

Since there's no way to check if an EditText already has a TextWatcher set, I've built my own solution with a boolean, that checks if the TextWatcher has already been added or not.

private EditText mEditText;
private TextWatcher mTextWatcher;
private boolean mTextWatcherIsEnabled = false;

public void setTextWatcher(TextWatcher textWatcher) {
    mTextWatcher = textWatcher;
}

public void enableTextWatcher() {
    if (!mTextWatcherIsEnabled) {
        mEditText.addTextChangedListener(mTextWatcher);
    }
    mTextWatcherIsEnabled = true;
}

public void disableTextWatcher() {
    if (mTextWatcherIsEnabled) {
        mEditText.removeTextChangedListener(mTextWatcher);
    }
    mTextWatcherIsEnabled = false;
}

For me this was happening because I had two different TextWatchers added to the same view, and the one I added first was removing itself:

view.addTextChangedListener(new ListenerA());
view.addTextChangedListener(new ListenerB());
class ListenerA extends TextWatcher {

    @Override
    public void beforeTextChanged(CharSequence s, int i, int i1, int i2) {
        view.removeTextChangedListener(this);
        // ...
    }
}

Because the first one removes itself, it causes an index out of bounds exception here:

private void sendBeforeTextChanged(CharSequence text, int start, int before, int after) {
    if (mListeners != null) {
        final ArrayList<TextWatcher> list = mListeners;
        final int count = list.size();
        for (int i = 0; i < count; i++) {
            list.get(i).beforeTextChanged(text, start, before, after);
        }
    }
    // ...
}

count is queried and the list has size of 2. So it calls get(0) which then removes itself. Then it calls get(1), but the list size is only 1 now, so this throws the exception.

I solved this by not actually removing my TextWatcher in the first place, and instead just disabling the behavior. You could alternatively solve this by guaranteeing that only the last watcher in the list ever removes itself.