Skip to content

fix(JsBridgeImpl): fire OnJsBridgeReady only once per JS context (#35)#42

Open
jim-daf wants to merge 1 commit intopengwei1024:masterfrom
jim-daf:fix/issue-35-onjsbridgeready-once
Open

fix(JsBridgeImpl): fire OnJsBridgeReady only once per JS context (#35)#42
jim-daf wants to merge 1 commit intopengwei1024:masterfrom
jim-daf:fix/issue-35-onjsbridgeready-once

Conversation

@jim-daf
Copy link
Copy Markdown

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

OnJsBridgeReady firing multiple times (refs #35)

Resolves #35

Why it fires more than once

JsBridgeImpl.getInjectJsString ends with an unconditional ready call:

builder.append("};");
builder.append("window." + newProtocol + "=new " + className + "();");
builder.append(newProtocol + ".OnJsBridgeReady();");
return builder.toString();

The script is pushed in by onInjectJs, which the host app typically calls from WebViewClient.onPageFinished or WebChromeClient.onProgressChanged. Both can fire more than once for a single navigation (sub frame finish, redirect, late content). Every extra invocation re-runs the whole script, which re-creates the bridge object and re-fires OnJsBridgeReady, so any JS handler registered on it executes once per injection. That is exactly the symptom in the issue.

The fix

Wrap the whole injection in a JS guard keyed on the configured protocol:

private String getInjectJsString() {
    StringBuilder builder = new StringBuilder();
    builder.append("if(!window.").append(newProtocol).append("){");
    builder.append("var ").append(className).append("=function(){");
    ...
    builder.append("window." + newProtocol + "=new " + className + "();");
    builder.append(newProtocol + ".OnJsBridgeReady();");
    builder.append("}");
    return builder.toString();
}

Behavior:

  • Genuine new page load. The JS context is fresh, so window.<protocol> is undefined, the guard passes, the bridge is installed, OnJsBridgeReady fires once. Same as today.
  • Repeated injection on the same context. window.<protocol> already references the bridge instance, the guard short-circuits, the script does nothing. OnJsBridgeReady does not fire again.
  • clean() already nulls the protocol (it evaluates a JS assignment that sets window.<protocol> back to undefined) so a deliberate re-init still works.

Files changed

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

…gwei1024#35)

The injected bridge script ended with an unconditional
"<protocol>.OnJsBridgeReady();" call. Re-running injectJs on the
same JS context (for example WebViewClient.onPageFinished firing
multiple times for the same page or sub frames triggering an extra
inject) re-fired the ready callback, so any JS handler registered
on it ran once per injection.

This change wraps the injection in
  if (!window.<protocol>) { ... }
which makes a genuine new page load (fresh JS context) still set
the bridge up exactly once, and turns repeat injection into a
no-op so OnJsBridgeReady fires only on the actual ready transition.
@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 addresses issue #35 where OnJsBridgeReady can fire multiple times for a single navigation due to repeated JS injection calls (e.g., multiple onPageFinished/subframe events), by making the injection idempotent per JS context.

Changes:

  • Wrap the injected bridge installation script in a if (!window.<protocol>) { ... } guard so the bridge is created and OnJsBridgeReady is fired only once per JS context.

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

Comment on lines +223 to 225
builder.append("if(!window.").append(newProtocol).append("){");
builder.append("var ").append(className).append("=function(){");
// 注入通用方法
Copy link

Copilot AI Apr 26, 2026

Choose a reason for hiding this comment

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

PR description mentions clean() sets window.<protocol> back to undefined, but the implementation uses <protocol>=undefined; (no window.). To avoid confusion and keep the contract clear, update either the description or the code so both refer to the same form (window.<protocol> vs global identifier).

Copilot uses AI. Check for mistakes.
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.

window.onJsBridgeReady 回调好多次怎么破?

2 participants