{"record":{"id":"302e3996e19fb4c3","repo":"Tencent/QMUI_Android","slug":"don-t-call-onenteranimationstart-directly","errorCode":null,"errorMessage":"don't call #onEnterAnimationStart() directly","messagePattern":"don't call #onEnterAnimationStart\\(\\) directly","errorType":"exception","errorClass":"IllegalAccessError","httpStatus":null,"severity":"error","filePath":"arch/src/main/java/com/qmuiteam/qmui/arch/QMUIFragment.java","lineNumber":1395,"sourceCode":"    public void runAfterResumed(Runnable runnable) {\n        Utils.assertInMainThread();\n        if (isResumed()) {\n            runnable.run();\n        } else {\n            if (mPostResumeRunnableList == null) {\n                mPostResumeRunnableList = new ArrayList<>(4);\n            }\n            mPostResumeRunnableList.add(runnable);\n        }\n    }\n\n    /**\n     * may not be call.\n     * @param animation\n     */\n    protected void onEnterAnimationStart(@Nullable Animator animation) {\n        if (mCalled) {\n            throw new IllegalAccessError(\"don't call #onEnterAnimationStart() directly\");\n        }\n        mCalled = true;\n        mEnterAnimationStatus = ANIMATION_ENTER_STATUS_STARTED;\n        isInEnterAnimationLiveData.setValue(true);\n    }\n\n    /**\n     * may not be call.\n     * @param animation\n     */\n    protected void onEnterAnimationEnd(@Nullable Animator animation) {\n        if (mCalled) {\n            throw new IllegalAccessError(\"don't call #onEnterAnimationEnd() directly\");\n        }\n        mCalled = true;\n        mEnterAnimationStatus = ANIMATION_ENTER_STATUS_END;\n        isInEnterAnimationLiveData.setValue(false);\n        notifyDelayRenderRunnableList();","sourceCodeStart":1377,"sourceCodeEnd":1413,"githubUrl":"https://github.com/Tencent/QMUI_Android/blob/026e7d486677d6593a96689cd590ec3561176717/arch/src/main/java/com/qmuiteam/qmui/arch/QMUIFragment.java#L1377-L1413","documentation":"QMUIFragment.onEnterAnimationStart() is a lifecycle callback invoked by the framework when the fragment's enter animation begins. It uses a mCalled flag so that invoking it manually is detected; a second/direct call throws IllegalAccessError because the enter-animation status (mEnterAnimationStatus, isInEnterAnimationLiveData) would be corrupted by duplicate invocation.","triggerScenarios":"Calling fragment.onEnterAnimationStart(animator) directly from app code, or invoking it twice (the mCalled flag is not reset between calls in the visible region), or subclass code invoking super/onEnterAnimationStart outside the framework dispatch path.","commonSituations":"Developers trying to manually trigger enter-animation behavior, custom animation orchestration that re-fires the callback, or reflection-based testing that calls protected lifecycle hooks directly.","solutions":["Remove any direct call to onEnterAnimationStart(); let QMUIFragment's internal animation dispatch call it.","If you need to hook the enter animation, override onEnterAnimationStart() in your fragment subclass and call super, instead of invoking it externally.","Trigger animation-driven behavior via the public fragment transition APIs (e.g. startFragment/postAnimateShow) rather than the callback.","For testing, verify the callback fired by observing isInEnterAnimationLiveData instead of calling it."],"exampleFix":"// before\nfragment.onEnterAnimationStart(animator);\n// after\n@Override\nprotected void onEnterAnimationStart(@Nullable Animator animation) {\n    super.onEnterAnimationStart(animation);\n    // custom logic here\n}","handlingStrategy":"validation","validationCode":"if (fragment instanceof QMUIFragment) {\n    // never call onEnterAnimationStart directly; rely on framework dispatch\n}","typeGuard":"boolean isFrameworkLifecycleHook(Method m) {\n    return m != null && java.lang.reflect.Modifier.isProtected(m.getModifiers())\n        && m.getName().startsWith(\"onEnterAnimation\");\n}","tryCatchPattern":null,"preventionTips":["Never invoke protected lifecycle callbacks (onEnterAnimationStart/End) from app code","Override the hooks and call super instead of calling them externally","Trigger transitions through public QMUI navigation APIs","Observe isInEnterAnimationLiveData for animation state"],"tags":["android","lifecycle","reflection-misuse","qmui-fragment"],"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"}