{"record":{"id":"3250ec105d9f5dd4","repo":"Tencent/QMUI_Android","slug":"can-not-add-skinchangelistener-while-dispatching","errorCode":null,"errorMessage":"Can not add skinChangeListener while dispatching","messagePattern":"Can not add skinChangeListener while dispatching","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"qmui/src/main/java/com/qmuiteam/qmui/skin/QMUISkinManager.java","lineNumber":641,"sourceCode":"                } else if (item instanceof Window) {\n                    dispatch(((Window) item).getDecorView(), index);\n                } else if (item instanceof View) {\n                    dispatch((View) item, index);\n                }\n            }\n        }\n\n        for (int i = mSkinChangeListeners.size() - 1; i >= 0; i--) {\n            OnSkinChangeListener item = mSkinChangeListeners.get(i);\n            item.onSkinChange(this, oldIndex, mCurrentSkin);\n        }\n        mIsInSkinChangeDispatch = false;\n    }\n\n    @MainThread\n    public void addSkinChangeListener(@NonNull OnSkinChangeListener listener) {\n        if (mIsInSkinChangeDispatch) {\n            throw new RuntimeException(\"Can not add skinChangeListener while dispatching\");\n        }\n        mSkinChangeListeners.add(listener);\n    }\n\n    public void removeSkinChangeListener(@NonNull OnSkinChangeListener listener) {\n        if (mIsInSkinChangeDispatch) {\n            throw new RuntimeException(\"Can not add skinChangeListener while dispatching\");\n        }\n        mSkinChangeListeners.remove(listener);\n    }\n\n    public int getCurrentSkin() {\n        return mCurrentSkin;\n    }\n\n    public interface OnSkinChangeListener {\n        void onSkinChange(QMUISkinManager skinManager, int oldSkin, int newSkin);\n    }","sourceCodeStart":623,"sourceCodeEnd":659,"githubUrl":"https://github.com/Tencent/QMUI_Android/blob/026e7d486677d6593a96689cd590ec3561176717/qmui/src/main/java/com/qmuiteam/qmui/skin/QMUISkinManager.java#L623-L659","documentation":"QMUISkinManager.addSkinChangeListener() refuses to add a listener while the manager is in the middle of dispatching a skin change (mIsInSkinChangeDispatch == true). Allowing re-entrant listener mutation during dispatch would cause ConcurrentModificationException or inconsistent notification, so it throws a RuntimeException instead.","triggerScenarios":"Calling addSkinChangeListener() from inside an OnSkinChangeListener callback or from skin-refresh code (e.g. a view's skin handler) that runs synchronously during changeSkin dispatch.","commonSituations":"A listener implementation that lazily registers another listener; skin listener trying to register an analytics listener triggered by the change event itself.","solutions":["Defer the addSkinChangeListener call until after dispatch, e.g. view.post(() -> manager.addSkinChangeListener(l)).","Register all listeners during initialization, before any changeSkin call.","In the listener callback, post the registration to the main queue instead of calling it synchronously."],"exampleFix":"// before\nmanager.addSkinChangeListener((manager2, old, now) -> {\n    manager2.addSkinChangeListener(analyticsListener); // throws: inside dispatch\n});\n\n// after\nmanager.addSkinChangeListener((manager2, old, now) -> {\n    view.post(() -> manager2.addSkinChangeListener(analyticsListener));\n});","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    skinManager.addSkinChangeListener(listener);\n} catch (RuntimeException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"while dispatching\")) {\n        new Handler(Looper.getMainLooper()).post(() -> skinManager.addSkinChangeListener(listener));\n    } else throw e;\n}","preventionTips":["Register all OnSkinChangeListener instances during initialization, never inside callbacks","Defer registrations made in response to skin changes via view.post or Handler","Avoid adding listeners from custom views' skin-refresh handlers"],"tags":["android","qmui","skin","reentrancy","illegal-state"],"backgroundTag":"invalid-state-transition","analyzedSha":"026e7d486677d6593a96689cd590ec3561176717","analyzedAt":"2026-09-06T13:32:24.816Z","contentChangedAt":"2026-09-06T13:32:24.816Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}