2. AGENDA
> Putting JavaFX (Mobile) in context
> My case study
> Main features of JavaFX (Mobile)
> JavaFX Mobile through a few examples
> Current status of JavaFX Mobile
> Conclusion
2
3. About the speaker
> Fabrizio.Giudici@tidalwave.it
> Senior Architect, Mentor, Technical Writer
> Working with Java (all the editions, plus exotic stuff) since 1996
> Working with Java ME since 1999
– First J2ME edition at JavaOne™ (Palm)
– Developed a couple of research projects with the University of Genoa
– Developed a couple of customer projects with my former company
– Consulting for customer projects
– Author of mobile FLOSS: windRose and blueBill Mobile
> Co-leader of JUG Milano
3
4. What's wrong with JME
> Everything was so good at the old, Palm times
– Small devices → no big expectations
– One reference platform
– One pervasive runtime
> With more devices, JME got fragmented
– JSR as extension points (e.g. bluetooth, location)
– Multiple combinations
– Harder and harder to test
– windRose pain
4
5. Hoping in Java FX Mobile
> JavaFX announced by Sun Microsystems in 2007
– A specific scripting language + a runtime
– Fight the “Ugly Java UI stereotype”
– Re-designed UI controls
– Integration with graphics designers workflow (e.g. NetBeans + Photoshop)
– Profiles for multiple targets (desktop / web, mobile)
> JavaFX Mobile profile
– Can sit on top of JME (MSA: Mobile Service Architecture, a.k.a. JSR-248)
– Maybe no more (or reduced) fragmentation?
> Oracle commitment after the Sun buy
> A fulfilled promise?
– Answers at the end :-) 5
6. My case study: blueBill Mobile
> “The field tool for birdwatchers”
– Records geotagged observations
– Reference guides with multimedia
– Social capabilities (à la Google Friends)
> Started in 2009 with JavaFX
> Currently the most advanced version is the Android one
– JavaFX version being redesigned (more later)
> Demo
6
7. Main features of JavaFX
> The languages is both imperative and declarative
– Imperative: for normal processing
– Declarative: mostly for the UI (no XML, no separate layout descriptor)
Binding
– Has got closures, mixins
> Compiles to the same VM bytecode
– Can mix with Java code (e.g. reference a Java library)
– On the desktop, can be mixed with Swing (in addition to its own UI widgets)
– On the mobile, runs its own UI widgets
7
8. Some code examples
> Example 1: Calling JME code
> Example 2: Posting contents (REST)
> Example 3: Getting resources (REST)
> Example 4: Binding (if time allows)
8
9. Example: calling JME code
> Not much to say: it just works
package it.tidalwave.bluebillmfx;
import javax.microedition.location.Criteria;
import javax.microedition.location.LocationProvider;
import javafx.animation.Timeline;
import javafx.animation.KeyFrame;
import it.tidalwave.geo.mapfx.model.Coordinates;
public class PositionTracker
{
public-read var currentPosition : Coordinates;
def criteria = new Criteria();
var locationProvider : LocationProvider;
postinit {
locationProvider = LocationProvider.getInstance(criteria);
pollingTimeline.play();
}
9
10. Example: calling JME code
> Need to copy data in JavaFX classes if you want binding support
function getPosition(): Void {
def location = locationProvider.getLocation(5); // JME stuff
def coordinates = location.getQualifiedCoordinates();
if (coordinates != null ) {
def lat = coordinates.getLatitude();
def lon = coordinates.getLongitude();
currentPosition = Coordinates { latitude: lat; longitude: lon };
}
}
def pollingTimeline = Timeline {
repeatCount: Timeline.INDEFINITE
keyFrames: KeyFrame { time: 10s; action: periodicTask }
};
function periodicTask(): Void {
getPosition();
// update the map position, etc...
}
} 10
11. Example: posting contents
> HttpRequest provides a skeleton
public class ObservationRDFUpload extends HttpRequest
{
public-init var serviceURL = "http://myHost:8084/blueBillGeoService";
override public var method = HttpRequest.POST;
public var content : String;
override var output on replace {
if (output != null) {
output.write(content.getBytes());
output.close();
}
}
override function start(): Void {
location = "{serviceURL}/postObservation"; // REST call
setHeader("Content-Type", "application/rdf");
super.start();
}
} 11
12. Example: posting contents
> HttpRequest provides a skeleton
def upload = ObservationRDFUpload
{
content: // ... assemble the string with the contents
};
upload.start();
12
13. Example: reading resources
> The relevant model class
public class Taxon
{
public-read protected var id : String;
public-read var urlsLoaded = false;
var urlsLoading = false;
var imageURLs : String[]; // [] is a list (“sequence”), not an array
var images : DeferredImage[];
public bound function getImage (index : Integer): DeferredImage {
def dummy = loadImageURLs(); // assignment is a requirement for the bound function
return images[index];
}
public bound function imageCount(): Integer {
return sizeof images;
}
13
14. Example: reading resources
> Getting a JSON resource catalog
function loadImageURLs(): Boolean {
if (not urlsLoading and not urlsLoaded) {
urlsLoading = true;
def fixedId = clean(id); // get rid of/escape “bad” chars such as :, #, etc...
def url = "http://kenai.com/svn/bluebill-mobile~media/catalog/{fixedId}.json";
def request : HttpRequest = HttpRequest { // mere instantiation, not subclassing!
location: url;
method: HttpRequest.GET;
onInput: function (is: InputStream) { // hey, this is not a switch() label!
if (request.responseCode != 200) {
imageURLs = "media/black_glossy_web20_icon_012.png";
}
else {
try {
def parser = PullParser { documentType: PullParser.JSON;
input: is; onEvent: parseEventCallback };
parser.parse();
}
catch (e: Exception) {
println("Exception {e} while parsing {url}");
}
finally {
is.close(); 14
}
}
}
15. Example: reading resources
> Getting a JSON resource catalog
onException: function (e: Exception) {
imageURLs = "media/black_glossy_web20_icon_012.png";
urlsLoaded = true;
}
onDone: function() {
images = for (url in imageURLs) {
DeferredImage { url: url };
}
imageURLs = null;
urlsLoaded = true;
}
}
request.start();
}
return urlsLoaded;
}
def parseEventCallback = function (event: Event): Void {
if ((event.type == PullParser.TEXT) and (event.name == "url")) {
insert event.text into imageURLs;
} 15
}
}
16. Example: reading resources
> Loading an image
> JavaFX Image loads automatically when initialized
var imageCounter = 0;
public class DeferredImage
{
public-init var url : String;
public-read var progress = bind image.progress;
public-read var error = bind image.error;
def id = imageCounter++;
public-read var image : Image;
public function load() { // start loading only when load() is called
if (image == null)
{
image = Image {
url: url
backgroundLoading: true
}
}
} 16
}
17. Example: binding
> Introducing TaxonSearchController
– Manages a list of Taxons
– Produces a filtered list of Taxons whose names match a substring
– Provides a hint for autocompletion
Eg. you type “He”
That matches only “Heron ***”
Hints is “Heron “
17
18. Example: binding
> Some relevant parts of Taxon
public class Taxon
{
public-read protected var displayName : String;
public-read protected var scientificName : String;
public-read protected var id : String;
override function toString() {
return "{displayName} ({scientificName}) ({id})"
}
}
18
19. Example: binding
> TaxonSearchController (1/3)
public class TaxonSearchController
{
public var taxons: Taxon[];
public var filteredTaxons: Taxon[];
public var selectedTaxonIndex : Integer = -1; // set from the UI
public var selectedTaxon = bind
if (selectedTaxonIndex < 0) then null
else filteredTaxons[selectedTaxonIndex];
// setting this property will trigger filtering
public var filter = "" on replace {
filteredTaxons = taxons[taxon | matches(taxon, filter)];
updateHint();
}
public-read var hint = ""; // the auto-completed hint
19
20. Example: binding
> TaxonSearchController (2/3)
protected function matches (taxon : Taxon, string: String) : Boolean {
if (string == "") {
return true;
}
if (taxon.displayName.toLowerCase().startsWith(string.toLowerCase())) {
return true;
// more complex in the real case, as it also deals with scientific name
// but not relevant now
}
return false;
}
20
21. Example: binding
> TaxonSearchController (3/3)
protected function updateHint(): Void {
def hintTry = commonLeadingSubstring(filteredTaxons);
//
// Sometimes it can't find a better auto-completion than the current filter,
//
hint = if (hintTry.length() > filter.length())
then hintTry
else filter;
}
}
21
22. Example: binding
> TaxonSearchScreen
public class TaxonSearchScreen extends Container // e.g. a UI panel
{
// warning: there are some simplifications, but the concepts are here
public-read def controller = TaxonSearchController {
selectedTaxonIndex: bind list.selectedIndex;
}
def list = ListBox { // the list widget in the UI
items: bind controller.filteredTaxons
};
def hint = bind controller.hint on replace {
if (hint != "") {
filter = hint;
}
}
var resetSelectionWhenFilterChanges = bind controller.selectedTaxon on replace {
if (controller.selectedTaxon != null) {
list.selectedIndex = 0;
}
}
TextBox { // a text input box in the UI
text: bind controller.filter with inverse
}
Text { // a label rendering a text in the UI
content: bind "{sizeof controller.filteredTaxons} specie(s)" 22
}
23. JavaFX mobile in 2009
> Development tools available (emulator, etc...)
> First capable phones at J1: LG Incite, HTC Touch Diamond
> Announced a “JavaFX Mobile player”
– Would run JavaFX on MSA (JSR 248) JME phones
> Looked very promising
– That's why blueBill Mobile started with it
23
24. Status updates in 2010
> Barcelona Mobile World Congress 2010
– “Feature phones” (Qualcomm and the Brew Mobile Platform OS)
– Sony-Ericsson in “upcoming phones”
> Recent phone status update (JavaFX forums, March 16, 2010)
– LG Incite, HTC Touch Diamond... still!
– “Unofficially” runs on any Windows Mobile 6.x device (e.g. HTC HD2)
> JavaFX 1.3 released on May 14, 2010
– Shares many improvements with the desktop/web profile
– Mobile emulator runs on Mac OS X
> JavaFX Mobile Player MIA (I mean, it's Windows Mobile only)
> No further manufacturer endorsements?
24
25. My view of Java-related mobile solutions
> We're in the climax of the “next generation mobile” war
– iPhone/iPad, Android leading on the growth rates
– Adobe Flash at stake, but if it survives will be relevant
> Is there still room for JavaFX Mobile? JME?
> JavaFX Mobile
– Still excites me, technically
– The Oracle-Sun deal might have slowed down deals – but...
– … timing out this Summer
–Hoping in a specific Android port/adaptation (using the Android UI/runtime)
Hearing a few people talking about that
Would bring-in e.g. the integration with graphic designer workflow
> JME still relevant for the “glorious old guard” (Nokia, etc..., BlackBerry)
– “Oldies but goodies” 25
26. My research for the Summer
> Two/threefold strategy
– Android (it delivers, short time-to-market)
– JME (catch the “old glorious guard”)
– JavaFX (if industry endorsements / Android support will show up)
> Study whether it is possible to reuse code
– Not thinking of “catch-all” frameworks
– Reusing only the models, writing ad-hoc UIs (no “GCD syndrome”)
– Maven for re-using artifacts
– Byte-code manipulation (e.g. RetroWeaver) for fitting JME Java 1.3?
– Follow my blog and my space at Dzone.com
> The “new” blueBill Mobile for JavaFX code is not yet available
– Under heavy refactoring, catching up with the Android version 26
27. Questions?
> Thanks for your attention
– http://javafx.com
– http://bluebill.tidalwave.it/mobile
– http://windrose.tidalwave.it
27