The Google Web Toolkit compiler is a marvelous tool for turning Java code into highly optimized Javascript, but prior to GWT 1.5, there was no mechanism for developers to customize the packaging of the compiler output, or the bootstrap of the application.
C developers are used to the ability to override the entry point glue code ('start' method in crt1.o for example), or to package statically, dynamically, etc, so why not provide this capability for GWT developers as well?
This will allow GWT compiled code to packaged and bootstrapped into environments like Flash/AIR Apollo, Gears, Gadget Containers, and the example I'm going to show today: Android Offline/Online Hybrid applications.
What's a Syndroid
As part of my ongoing prototyping in Syndroid, I stumbled onto the idea of a hybrid Android application. One where some of the UI can be rendered via GWT in the embedded WebKit, and the other part, running as native Java/Dalvik code accessing Android APIs.
For example, you may wish to develop a Gadget with GWT, that runs in several containers, but when it runs on Android, it has additional access to information like Contacts, Location, etc. How can GWT code call native Android functions?
Generators Again? The Android Native Interface
Before showing you the magic sauce that permits this, let's look at an example API of how we might like this to work. This will all seem familiar to those who use GWT RPC. First, let's create an interface that will trigger a GWT Generator.
package org.timepedia.syndroid.client.android;
public interface AndroidNativeInterface {}
and a sample interface exposing a method to retrieve our GPS coordinates
package org.timepedia.syndroid.client.android;
@ImplementedBy(LocationServiceImpl.class)
public interface LocationService extends AndroidNativeInterface {
String getLocation();
}
next, Android implementation code (*warning, code may not work, I just typed it into the blog without testing)
package org.timepedia.syndroid.android;
public class LocationServiceImpl implements LocationService {
public Location getLocation() {
LocationManager lm = (LocationManager)
Context.getSystemService(Context.LOCATION_SERVICE);
return locationManager.getCurrentLocation("gps").toString();
}
}
finally, some GWT code that uses it
LocationService lService = (LocationService)GWT.create(LocationService.class);
Window.alert("My location is: "+lService.getLocation());
Great, but how does it work, and how do Linkers fit in?
There are a few ways that RPC calls between GWT and Android could work. For example, a traditional XHR request could be made to a HTTP service running on Android. But this would require the API to be asynchronous and have a high overhead.
It turns out, that the native WebKit that comes with Android has the capability to extend the native Javascript APIs available via Java. For example,
WebView wv=new WebView(this);
wv.getSettings().setJavaScriptEnabled(true);
wv.addJavascriptInterface(new LocationServiceImpl(), "locationService");
This binds our Android LocationServiceImpl instance to a globally scoped JavaScript object called 'locationService', allowing Javascript code to invoke the locationService.getLocation() method.
We now have two problems to solve, first, generate GWT code in a Generator that invokes this $wnd.locationService.getLocation() global object, and secondly, to generate Android bootstrap code (shown above), an AndroidManifest, and package up everything into a APK file that can be installed on a phone. The latter problem is what Linkers help solve.
The Generator's Job
I won't go into detail about how to implement the Generator, as it's been covered before in this blog, the Generator essentially creates a LocationServiceImpl class, with JSNI method getLocation(), which calls $wnd.locationService.getLocation()
The Linker's Job
The Linker has the bulk of the grunt work here. Here's what it must accomplish:
1. Generate Bootstrap Android application
2. For each generated AndroidNativeInterface, add Javascript bindings
3. Place all generated resources in proper Android asset layout
4. Create Android Manifest file
5. Add extra code to load the initial WebView start page with GWT app
6. Invoke Android tools to compile Dalvik code, and package APK files.
The Linker and LinkerContext interface in GWT 1.5 allow one to specify in a module file, which sets of Linkers are run. GWT 1.5 provides some standard ones like the IFrameLinker and XSLinker, which generate the ordinary selection scripts we see in 1.4. The LinkerContext interface provides a mechanism to discover the outputs from the compiler pass, and to emit new ones.
I'll have to end this installment here, because it is getting long and I need to get back to work, in the next installment, I'll show some actual Linker code for an AndroidGadgetLinker.