happydog-intj/JsBridge: a Java to JavaScript bridge for Android WebView
android java and javascript bridge, inspired by wechat webview jsbridge
At a glance
- What is it?
- JsBridge wires native Java and page JavaScript together inside an Android WebView, with named handlers, response callbacks and an optional host whitelist. It is a small library with a clear surface, and the parts it does not document are worth knowing before you adopt it.
- Who is it for?
- Adopt JsBridge if you are building an Android app whose WebView content needs to call native code and receive answers back, and if a named handler plus callback model fits how your pages are written. Skip it if you need an iOS or Flutter bridge, since this repository is Java and Android only, or if you want a documented release history, because the repository has no published releases.
- Can I use it commercially?
- Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
- Is it still maintained?
- Yes. The repository last received commits 63 days ago.
- What is it written in?
- Mainly Java, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.
Star history
- happydog-intj/JsBridge9,909 ★ · +2
| Repository | 2026-09-22 | 2026-11-14 |
|---|---|---|
| happydog-intj/JsBridge | 9.9k | 9.9k |
2 Stars in the last 53 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
The problem JsBridge solves for Android WebView apps
A plain Android WebView gives you two one-way channels. JavaScript can reach Java through methods annotated with @JavascriptInterface, and Java can push values into the page with evaluateJavascript. Neither channel carries an answer back on its own. If a page asks the native side for a token and the native side needs a second to fetch it, the page has no built-in way to wait for that value. Teams end up hand-rolling a request id, a map of pending callbacks and a JSON envelope, and that plumbing is where the bugs live.
JsBridge is that plumbing, packaged. It targets Android app developers who embed HTML in a WebView and need the page and the host app to talk in both directions. The README frames it as "a bridge between Java and JavaScript for Android WebView, providing safe bidirectional communication", and the repository is Java with an example app and a library module. The audience is narrow on purpose: if your UI is native only, or your page never needs to call the host, this library adds a layer you will not use.
How the bridge moves data between Java and JavaScript
The mechanism is named handlers plus a callback registry. On the Java side you call callHandler with a handler name, a data string and an OnBridgeCallback. On the JavaScript side a handler registered under that same name receives the data and a responseCallback, and whatever it passes to responseCallback comes back to the Java callback. The reverse direction works the same way: a class extending BridgeWebView.BaseJavascriptInterface exposes methods annotated with @JavascriptInterface, and the page calls them with WebViewJavascriptBridge.callHandler, passing a handler name, data and a response function.
Two details shape how you design around it. First, data crosses as a string, which is why the README wires in Gson and shows new Gson().toJson(user) before a call. The library does not pick a serialization format for you. Second, callbacks are removed after their first invocation by default. For anything that fires repeatedly, such as a stream of events, the README points at callHandlerPersistent, which keeps the callback alive across multiple invocations.
The bridge JavaScript is injected on page load, so page code cannot assume the object exists at parse time. The README's setupWebViewJavascriptBridge helper queues callbacks on window.WVJBCallbacks until window.WebViewJavascriptBridge appears, and there is also a WebViewJavascriptBridgeReady event for the same purpose. If you skip that wait and call the bridge immediately, you are racing the injection.
Installing JsBridge from JitPack and making a first call
The library is distributed through JitPack rather than Maven Central, so the repository has to be declared before the dependency. The README gives both blocks. Add the JitPack repository at the project level, then the dependency at the module level:
// settings.gradle or build.gradle (project level)
repositories {
maven { url "https://jitpack.io" }
}
// build.gradle (module level)
dependencies {
implementation 'com.github.happydog-intj:JsBridge:v2.1.0'
}With the dependency resolved, drop BridgeWebView into the layout. Note the package name in the README's XML: it is com.github.lzyzsd.jsbridge, not the happydog-intj group used in the dependency coordinate. That mismatch is worth checking in your own build before you file it as a bug.
<com.github.lzyzsd.jsbridge.BridgeWebView
android:id="@+id/webView"
android:layout_width="match_parent"
android:layout_height="match_parent" />In the Activity, set a Gson instance, register the interface object under the name WebViewJavascriptBridge, and load a page. The interface object is constructed with the bridge's callback maps and the bridge itself:
BridgeWebView webView = findViewById(R.id.webView);
webView.setGson(new Gson());
webView.addJavascriptInterface(
new MainJavascriptInterface(
webView.getCallbacks(),
webView.getPersistentCallbacks(),
webView),
"WebViewJavascriptBridge");
webView.loadUrl("file:///android_asset/demo.html");The first real use is a round trip. Register a handler in the page under a name, then call that name from Java and log what comes back. The README's example handler writes the incoming data into a DOM element and answers with a fixed string, which is enough to confirm both directions are live before you build anything on top.
The host whitelist and what its default actually means
JsBridge ships an origin check for the native side. addAllowedHost takes a host, and a leading wildcard covers subdomains, so "*.example.com" matches hosts beneath example.com. setAllowedHosts takes a Set<String> if you would rather declare the list once, and BridgeConfig carries the same capability when you want the rest of the bridge configuration in one object.
The default is the part to read twice. The README states that when the whitelist is empty, all origins are allowed, and calls this "fully backward compatible". That is an accurate description of the behaviour and also a real exposure: an app that never calls addAllowedHost will let any page loaded in that WebView reach the @JavascriptInterface methods, including pages reached through a redirect or a link the user taps. The check only becomes a boundary once you populate it. For an app that loads only bundled assets this may not matter. For one that loads remote content, an empty list is a decision, not a neutral default.
The same whitelist is available when you are not using BridgeWebView. The README shows webView.bridgeHelper.addAllowedHost("example.com") on a custom WebView subclass, which means the origin check is not tied to the bundled view class.
Using BridgeHelper when you cannot use BridgeWebView
BridgeWebView is convenient but it is still a WebView subclass, and some apps already have their own subclass with a WebViewClient that handles deep links, error pages or progress. Replacing it to get a bridge is a poor trade. BridgeHelper exists for that case. You implement IWebView on your subclass, hold a BridgeHelper instance, created with new BridgeHelper(this), and have your WebViewClient call into the helper, starting with onPageStarted.
The cost is that the integration lives in your own WebView subclass, so you write and maintain the glue between that class and the helper yourself.
One thing the helper path shares with BridgeWebView is the injection timing. Because the script is injected on page load, any page logic that runs earlier must use the WVJBCallbacks queue or the ready event. Custom WebView integrations that forget the queue tend to fail intermittently, which is harder to debug than failing always.
Where JsBridge is the wrong choice
The clearest limit is platform. This is an Android library written in Java. There is no iOS target and no Flutter plugin in the repository's top-level tree. If your product ships on both platforms, you are looking at two bridges with two handler models, and the JavaScript setup code is the only part you can share.
The second limit is the release story. The repository has no published GitHub releases and no homepage. The dependency coordinate pins a tag, v2.1.0, which is what the README shows, but there is no changelog in the repository to tell you what changed between tags or what a future upgrade would cost. For a library that sits between your app and arbitrary page content, that is a real gap. You should treat the version tag as the contract and read the diff yourself before moving it.
The third limit is the callback model itself. A callback that is removed after one invocation is the right default for request and response, and callHandlerPersistent covers repeated calls, but the README does not describe ordering guarantees across handlers or cancelling a pending call. If your page needs several native calls resolved in a defined order, or abandoned on navigation, check the library source for that behaviour before relying on it.
Finally, the licence is not stated anywhere in the repository files. That is not a small omission for a dependency you ship inside an app. Resolve it from the repository before you add the coordinate, not after.
JsBridge compared with writing your own @JavascriptInterface layer
The real alternative is not another library, it is the platform API you already have. Android's own @JavascriptInterface mechanism is what JsBridge's Java-side interface is built on, and for a small surface it is genuinely enough. If your page needs to call exactly one native method and never needs an answer, a single annotated method is fewer moving parts than a bridge, and it has no injection timing to reason about.
The difference shows up as the surface grows. A hand-rolled layer needs a naming scheme for methods, a request id generator, a map from id to pending callback, cleanup when a page navigates away, and a JSON envelope both sides agree on. JsBridge supplies the handler registry, the callback maps that BridgeWebView exposes through getCallbacks and getPersistentCallbacks, the injection lifecycle, and the origin check. What it does not supply is serialization, so you still bring Gson or your own format.
The trade is control against convention. A hand-rolled layer can be shaped to your app's exact needs, including the cancellation and ordering behaviour JsBridge does not offer. JsBridge gives you a documented handler-and-callback convention and a whitelist you would otherwise have to write and get right. If your WebView surface is one or two calls, write them yourself. If it is a growing set of named operations with responses, the convention is worth the dependency.
Maintenance status and what an upgrade costs
The repository is not archived, and the last push was on 2026-09-12. That is recent enough that the code is not abandoned, but there are no release notes, no changelog and no published releases, so recency of commits is the only maintenance signal available and it says nothing about what changed or why.
Upgrade cost is therefore unknown from the documentation. The README pins v2.1.0 in the dependency line, and the only safe way to move that pin is to read the diff between tags in the repository. Two areas are worth checking on any bump: the JavaScript injected by the bridge, because a change there affects page code you may not control, and the callback registry behaviour, because persistent versus one-shot callbacks are load-bearing in any app that uses both.
Editorial conclusion
Adopt JsBridge if you are building an Android app whose WebView content needs to call native code and receive answers back, and if a named handler plus callback model fits how your pages are written. Skip it if you need an iOS or Flutter bridge, since this repository is Java and Android only, or if you want a documented release history, because the repository has no published releases. Before you commit, verify two things in your own checkout: that the com.github.lzyzsd.jsbridge package name in the layout XML matches the artifact you actually resolve, and that your Gradle setup can reach JitPack. The whitelist default is the one behaviour to decide on deliberately, because an empty list allows every origin.
Frequently asked questions
What is JsBridge and what does it do?
JsBridge is an Android library that provides bidirectional communication between Java and JavaScript inside a WebView. It gives you named handlers on both sides, response callbacks, and an optional host whitelist for native methods.
What is a JsBridge method and how do I register one?
A handler is registered under a name and invoked by that name from the other side. On the Java side you call callHandler with an OnBridgeCallback, and in JavaScript you register with WebViewJavascriptBridge.registerHandler and answer through responseCallback.
How do I install JsBridge in an Android project?
Add the JitPack repository to your project level Gradle file, then add the dependency com.github.happydog-intj:JsBridge:v2.1.0 at the module level. After that you can place BridgeWebView in a layout or wire BridgeHelper into your own WebView subclass.
Why does the bridge JavaScript not exist when my page loads?
The bridge script is injected on page load, so window.WebViewJavascriptBridge may not be defined yet when your script runs. The README's setupWebViewJavascriptBridge helper queues callbacks on window.WVJBCallbacks, and there is also a WebViewJavascriptBridgeReady event to wait for.
Are JsBridge callbacks reusable after the first call?
By default a callback is removed after its first invocation. For scenarios that need multiple responses, such as real-time updates or event streams, the README documents callHandlerPersistent, which keeps the callback alive across invocations.
Does JsBridge restrict which pages can call native methods?
It can. addAllowedHost and setAllowedHosts restrict which origins may invoke @JavascriptInterface methods, and a wildcard such as "*.example.com" covers subdomains. When the whitelist is empty, which is the default, all origins are allowed.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/happydog-intj-jsbridge)Star history chart for your README
[](https://hysenlabs.com/projects/happydog-intj-jsbridge)Was this useful?