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:
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:
A more interesting situation is with transient objects returned from methods of an injected Java object. Consider the following example:
Again, the object returned from
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 tags). Each of these frames in fact has it own global context (and can even be prevented from accessing other frames). According to Java Bridge rules, each named object is injected into contexts of all frames. So if we imagine that we have loaded an HTML document with an into WebView, and then repeated the calls from above in both main document and the , we will have the following picture:
Note that as MyObject.getHandler() returns a new Handler instance every time, we have two instances of Handler (one per frame), but still have only one instance of MyObject.
To summarize the lifecycle topic, here is a state diagram of a Java object lifecycle from the Java Bridge's perspective:
Please also note that there is no way for a named injected object to become a transient one, although the opposite is possible.
Three major problems must be addressed by Java Bridge:
The first problem is in fact the easiest one. Type conversions are described in Sun Live Connect 2 spec, the only issue is that Java Bridge doesn't closely follow the spec (for compatibility with earlier versions?). Such deviations are marked as LIVECONNECT_COMPLIANCE in Java Bridge code and tests.
For dealing with method overloading, the spec proposes a cost-based model for methods resolution, where the “most suitable” Java overloaded method version is selected. Android Java Bridge implementation in fact simply selects an arbitrary overloaded method with the number of arguments matching the actual number of parameters passed to the interface method and then tries to coerce each value passed into the destination Java type. If there is no method with matching number of arguments, the method call fails.
Threading issues need to be considered when dealing with invocations of methods of injected objects. In accordance with the API definition, methods are invoked on a dedicated thread maintained by WebView.
The requirement for serving the requests on the background thread means that the following code must work (see https://crbug.com/438255):
To fulfill this, the browser UI thread must not be involved in the processing of requests from the renderer.
In JB MR1, the
java.lang.Object for all applications.
The next issue comes from the fact that injected Java objects are shared between frames. This allows frames, otherwise isolated (for example, due to cross-origin policy), to interact. For example, if an injected object has methods ‘storePassword’ and ‘getPassword’, then a password stored from one frame can be retrieved by another frame. To prevent this, instead of injecting an object itself, a stateless factory must be injected, so each frame will be creating its own set of Java objects.