Skip to content

fix(JBCallbackImpl): use evaluateJavascript so delayed callback.apply works (#34)#41

Open
jim-daf wants to merge 1 commit intopengwei1024:masterfrom
jim-daf:fix/issue-34-evaluatejavascript-callback
Open

fix(JBCallbackImpl): use evaluateJavascript so delayed callback.apply works (#34)#41
jim-daf wants to merge 1 commit intopengwei1024:masterfrom
jim-daf:fix/issue-34-evaluatejavascript-callback

Conversation

@jim-daf
Copy link
Copy Markdown

@jim-daf jim-daf commented Apr 26, 2026

Async callback.apply lost because loadUrl is unreliable (refs #34)

Resolves #34

Reproducer from the issue

The OP calls a native method, kicks off a 3 second delayed handler, and tries to deliver the result through the same JBCallback:

@Override
public void getReadInfo(String type, String code, String jsonString, JBCallback callback) {
    new Handler().postDelayed(() -> {
        callback.apply("s"); // does not reach the H5 page
    }, 3000);
    callback.apply("s");     // reaches the H5 page
}

Root cause

JBCallbackImpl.apply builds a JS snippet and pushes it to the WebView with loadUrl("javascript:..."):

mHandler.post(new Runnable() {
    @Override
    public void run() {
        if (method.getModule().mWebView instanceof WebView) {
            ((WebView) method.getModule().mWebView).loadUrl(builder.toString());
        } else if (method.getModule().mWebView instanceof IWebView) {
            ((IWebView) method.getModule().mWebView).loadUrl(builder.toString());
        }
    }
});

WebView.loadUrl("javascript:...") is the legacy API. It is reliable when called from inside an onJsPrompt (or shouldOverrideUrlLoading) handler because the WebView still has an active dispatch frame. Once the call is asynchronous (a postDelayed, a network callback, anything off the bridge entry frame) modern WebView builds drop or coalesce the URL load with no visible effect. That is exactly what the OP is hitting.

WebView.evaluateJavascript was added in API 19 to provide a reliable async-safe way to run JS in the active document. Using it here makes the delayed callback.apply arrive on the JS side every time.

The fix

Object webView = method.getModule().mWebView;
if (webView instanceof WebView) {
    WebView wv = (WebView) webView;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        String js = builder.toString();
        if (js.startsWith("javascript:")) {
            js = js.substring("javascript:".length());
        }
        wv.evaluateJavascript(js, null);
    } else {
        wv.loadUrl(builder.toString());
    }
} else if (webView instanceof IWebView) {
    ((IWebView) webView).loadUrl(builder.toString());
}

Notes:

  • evaluateJavascript wants raw script, not a javascript: URL, so the leading "javascript:" prefix that the existing builder emits is stripped before the call.
  • The pre-API-19 fallback keeps the original behavior for old devices.
  • The IWebView branch is left on loadUrl because that abstraction is implementer defined and we cannot assume an evaluateJavascript equivalent.

Files changed

  • jsbridge/src/main/java/com/apkfuns/jsbridge/JBCallbackImpl.java

… works (pengwei1024#34)

A synchronous callback.apply works because it runs while the JS
prompt is still pending and the WebView treats the loadUrl
javascript URL as part of the active call. A delayed apply (the
classic case is after a network request finishes) shows nothing
because loadUrl("javascript:...") is unreliable on modern WebView
when the call did not originate from a JS event.

evaluateJavascript was added in API 19 for exactly this scenario.
This change prefers it for android.webkit.WebView and keeps loadUrl
as a fallback for pre-19 builds and the IWebView wrapper.
@jim-daf jim-daf marked this pull request as ready for review April 26, 2026 10:06
Copilot AI review requested due to automatic review settings April 26, 2026 10:06
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes lost asynchronous JBCallback.apply() deliveries by switching from WebView.loadUrl("javascript:...") to WebView.evaluateJavascript(...) on API 19+ (keeping loadUrl as a fallback for older Android versions and for IWebView).

Changes:

  • Use WebView.evaluateJavascript for callback execution when running on API 19+.
  • Strip the "javascript:" prefix before calling evaluateJavascript (since it expects raw script).
  • Keep the existing loadUrl behavior for pre-KitKat devices and IWebView implementations.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

求教,延迟几秒之后callback.apply(),H5页面无法收到回调

2 participants