{"record":{"id":"9a11f82ee37927ae","repo":"Justson/AgentWeb","slug":"the-webview-related-to-the-jscallback-has-been-rec","errorCode":null,"errorMessage":"the WebView related to the JsCallback has been recycled","messagePattern":"the WebView related to the JsCallback has been recycled","errorType":"exception","errorClass":"JsCallbackException","httpStatus":null,"severity":"error","filePath":"agentweb-core/src/main/java/com/just/agentweb/JsCallback.java","lineNumber":51,"sourceCode":"    private WeakReference<WebView> mWebViewRef;\n    private int mIsPermanent;\n    private String mInjectedName;\n\n    public JsCallback(WebView view, String injectedName, int index) {\n        mCouldGoOn = true;\n        mWebViewRef = new WeakReference<WebView>(view);\n        mInjectedName = injectedName;\n        mIndex = index;\n    }\n\n    /**\n     * 向网页执行js回调；\n     * @param args\n     * @throws JsCallbackException\n     */\n    public void apply (Object... args) throws JsCallbackException {\n        if (mWebViewRef.get() == null) {\n            throw new JsCallbackException(\"the WebView related to the JsCallback has been recycled\");\n        }\n        if (!mCouldGoOn) {\n            throw new JsCallbackException(\"the JsCallback isn't permanent,cannot be called more than once\");\n        }\n        StringBuilder sb = new StringBuilder();\n        for (Object arg : args){\n            sb.append(\",\");\n            boolean isStrArg = arg instanceof String;\n            // 有的接口将Json对象转换成了String返回，这里不能加双引号，否则网页会认为是String而不是JavaScript对象；\n            boolean isObjArg = isJavaScriptObject(arg);\n            if (isStrArg && !isObjArg) {\n                sb.append(\"\\\"\");\n            }\n            sb.append(String.valueOf(arg));\n            if (isStrArg && !isObjArg) {\n                sb.append(\"\\\"\");\n            }\n        }","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/Justson/AgentWeb/blob/8f7f6adbf01010e9b2b44638f31edf10e1ba53f7/agentweb-core/src/main/java/com/just/agentweb/JsCallback.java#L33-L69","documentation":"JsCallback.apply throws JsCallbackException when the WeakReference to the WebView it holds has been cleared, i.e. the WebView was destroyed/recycled (typically after AgentWeb.destroy() or page teardown). The library cannot execute the JS callback without a live WebView, so further calls are impossible.","triggerScenarios":"Holding a JsCallback beyond the page/AgentWeb lifetime and calling apply() after the WebView was destroyed, or invoking from a delayed handler/thread after navigation completed and resources were recycled.","commonSituations":"Async work (network, timer) finishing after onDestroy and then attempting to call back into the page, caching JsCallback instances across page loads, or AgentWeb.destroy() being called on back-press while callbacks were pending.","solutions":["Check the WebView is alive before calling apply, and discard stale JsCallbacks","Cancel pending async work in onDestroyView/onDestroy so callbacks do not outlive the page","Re-issue the callback through a fresh JsCallback obtained during the current page load","Catch JsCallbackException and treat it as 'page gone' — log and skip"],"exampleFix":"// before\nhandler.postDelayed(() -> callback.apply(result), 5000); // webview may be gone\n// after\nhandler.postDelayed(() -> {\n    try { callback.apply(result); }\n    catch (JsCallbackException e) { Log.w(TAG, \"page already recycled\", e); }\n}, 5000);","handlingStrategy":"try-catch","validationCode":"if (callback == null || webViewRefCleared(callback)) { return; }","typeGuard":"boolean callbackAlive(JsCallback cb) { try { cb.apply(); return false; } catch (JsCallbackException e) { return false; } }","tryCatchPattern":"try { callback.apply(args); } catch (JsCallbackException e) { Log.w(TAG, \"webview recycled, drop callback\", e); }","preventionTips":["Cancel async tasks in onDestroyView","Don't cache JsCallback beyond page lifetime","Use lifecycle-aware scopes for callbacks","Treat JsCallbackException as a normal teardown signal"],"tags":["android","javascript-bridge","webview-recycled","async-callback"],"backgroundTag":"invalid-state-transition","analyzedSha":"8f7f6adbf01010e9b2b44638f31edf10e1ba53f7","analyzedAt":"2026-09-11T10:05:24.337Z","contentChangedAt":"2026-09-11T10:05:24.337Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}