microg/GmsCore · error · IllegalStateException

Cannot add twice the same OnSwitchChangeListener

Error message

Cannot add twice the same OnSwitchChangeListener

What it means

SwitchBar keeps a list of OnSwitchChangeListener objects and refuses to register the same listener instance twice. Adding a duplicate would cause the listener to receive every checked-state change twice, so addOnSwitchChangeListener throws an IllegalStateException when contains(listener) is already true.

Source

Thrown at play-services-core/microg-ui-tools/src/main/java/org/microg/tools/ui/SwitchBar.java:185

        final boolean isChecked = !mSwitch.isChecked();
        setChecked(isChecked);
    }

    public void propagateChecked(boolean isChecked) {
        final int count = mSwitchChangeListeners.size();
        for (int n = 0; n < count; n++) {
            mSwitchChangeListeners.get(n).onSwitchChanged(mSwitch, isChecked);
        }
    }

    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        propagateChecked(isChecked);
    }

    public void addOnSwitchChangeListener(OnSwitchChangeListener listener) {
        if (mSwitchChangeListeners.contains(listener)) {
            throw new IllegalStateException("Cannot add twice the same OnSwitchChangeListener");
        }
        mSwitchChangeListeners.add(listener);
    }

    public void removeOnSwitchChangeListener(OnSwitchChangeListener listener) {
        if (!mSwitchChangeListeners.contains(listener)) {
            throw new IllegalStateException("Cannot remove OnSwitchChangeListener");
        }
        mSwitchChangeListeners.remove(listener);
    }

    static class SavedState extends BaseSavedState {
        boolean checked;
        boolean visible;

        SavedState(Parcelable superState) {
            super(superState);
        }

View on GitHub (pinned to 157c9d86ac)

Solutions

  1. Ensure addOnSwitchChangeListener is called only once per listener instance, e.g. guard with a boolean flag or add in onCreate only.
  2. Always pair with removeOnSwitchChangeListener in the symmetric lifecycle callback (onDestroy/onPause) before re-adding.
  3. Check mSwitchChangeListeners.contains(listener) before adding if you cannot control call sites.
  4. If duplicate registration is intentional, maintain your own list and skip SwitchBar's duplicate check.

Example fix

// before
switchBar.addOnSwitchChangeListener(this);
// after
if (!switchBarContainsListener) {
    switchBar.addOnSwitchChangeListener(this);
    switchBarContainsListener = true;
}
Defensive patterns

Strategy: validation

Validate before calling

if (listener != null && !switchBarContainsListener) { switchBar.addOnSwitchChangeListener(listener); switchBarContainsListener = true; }

Try / catch

try { switchBar.addOnSwitchChangeListener(listener); } catch (IllegalStateException e) { // already registered; ignore }

Prevention

When it happens

Trigger: Calling addOnSwitchChangeListener(listener) with a listener instance that was already added and not removed; typical when an Activity/Fragment re-runs setup code (e.g. onResume or configuration change) without calling removeOnSwitchChangeListener first.

Common situations: Lifecycle mistakes in Android Activities/Fragments: adding the listener in onCreate and again in onResume; re-attaching after rotation; adding an anonymous listener recreated each call (a new instance is fine, but the same instance re-added is not).

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of microg/GmsCore@157c9d86ac (2026-09-06). Data as JSON: /api/errors/77c9626969f0fa1d. Report an issue: GitHub.