[TOC]
This page explains ideas behind the Java ↔ JavaScript bridge implementation. This is to ensure that important use cases and scenarios, which must be preserved regardless of how the bridge is implemented, are captured. The need for this description arose while migrating the NPAPI-based implementation to a Gin-based implementation. Although a vast number of unit tests already existed, they still didn’t cover all important aspects of the bridge behavior and we had to add some new tests to ensure we are preserving compatibility.
The Gin implementation was introduced in Chromium M37 (initial Android Lollipop release), with the threading issue fixed in M39 (L MR1).
An API for embedders is exposed on android.webkit.WebView class:
Important notes as defined by the API:
@JavascriptInterface
are exposed to JavaScript code; Java object fields are never exposed;Argument and return values conversions are handled after Sun Live Connect 2 spec. In fact, there are lots of deviations from it (probably, to preserve compatibility with earlier WebView versions). What can pass the boundary between VMs is somewhat limited. This is what is allowed:
The purpose of Java bridge is to establish interaction between two virtual machines (VMs): Java and JavaScript. Both VMs employ a similar approach to managing objects lifetime—VMs gather and dispose unreferenced objects during garbage collection (GC) cycles. The twist that Java bridge adds is that objects in one VM can now virtually reference (and prevent from being disposed) objects from another VM. Let us consider the following Java code:
// in Java
webView.addJavascriptInterface(new MyObject(), "myObject");
The instantiated MyObject is now being virtually hold by its JavaScript counterpart, and is not garbage-collected by Java VM despite the fact that there are no explicit references to it on the Java side. The MyObject instance is kept referenced until the action of the addJavascriptInterface call is cancelled by a call to removeJavascriptInterface:
// in Java
webView.removeJavascriptInterface("myObject");
A more interesting situation is with transient objects returned from methods of an injected Java object. Consider the following example:
// in Java
class MyObject {
class Handler {
}
@JavascriptInterface
public Object getHandler() { return new Handler(); }
}
Again, the object returned from getHandler
method is not explicitly
referenced on the Java side, albeit it should not be disposed until it is in use
on the JavaScript side. The «in use» period is determined by the lifetime of the
JavaScript interface object that has been implicitly created as a result of a
call to getHandler from JavaScript. That means, the instance of Handler on
the Java side should be kept alive during the period while the corresponding
JavaScript interface object is still referenced:
// in JavaScript
{
...
let handler = myObject.getHandler();
...
}
The following figure illustrates relationships between Java and JavaScript objects created in the previous examples:
Note that Java and JavaScript VMs are absolutely independent and unaware of each other’s existence. They can work in different processes and, in theory, even on different physical machines. Thus, the depicted references from JavaScript objects to Java objects are virtual—they don’t exist directly. Instead, it is the Java bridge who holds the Java objects for as long as it is needed. We would like to depict that, but first we need to consider the whole picture.
So far, we were thinking about Java bridge in abstract terms. But in fact, it is used in the context of a WebView-based application. The Java side of the bridge is tightly coupled to an instance of WebView class, while bridge’s JavaScript side is bound to a HTML rendering engine. This is further complicated by the facts that in the Chromium architecture renderers are isolated from their controlling entities, and that Chromium is mainly implemented in C++, but needs to interact with Android framework which is implemented in Java.
Thus, if we want to depict the architecture of Java bridge, we also need to include parts of the Chromium framework that are glued to Java bridge:
The figure is now much scarier. Let’s figure out what is what here:
Set<Object>
) that holds all injected objects to prevent their
collection. Note that WebView class manages a C++ object called
WebContents (in fact, the relationship is more complex, but these details
are not relevant for us). As Java Bridge implementation is in C++, the
retaining set is actually managed by the C++ side, but objects from the
native side do not hold any strong references to it, as that would create a
cyclic reference and will prevent the WebView instance from being collected.The diagram above misses one more important detail. WebView can load a complex HTML document consisting of several frames (typically inserted using