Jolyon Smith has been posting a lot of really rosy stuff about Oxygene lately. And he even commented on a recent blog post of mine, in response to my frustration about Embarcadero blatantly ripping off paying customers by not including Android support as part of the baseline Delphi installation:
[Oxygene is] a better Pascal even than Delphi these days and fully supports Android (and iOS) development using the platform native frameworks, so you get to learn “proper” Android development (portable skills) without having to swallow the Java pill.
Thing is, I’ve been doing some serious looking at Oxygene too lately. I haven’t reached all of the same conclusions he has, though. But then again, I’ve been trying to do different stuff.
I have to say, though, his statement that you can use it for Android development “without having to swallow the Java pill” is IME completely incorrect. Because Oxygene’s Android support is a subset of Oxygene for Java, focused entirely on Dalvik support and not native code like Delphi or Oxygene’s iOS compiler, it’s still the same old Java pill, just with a Pascal-flavored candy coating. It means you still have all of the JVM’s ugly technical limitations, like no value types and a severely broken and limiting Generics model. And you’ve still got to work with all of the Android system libraries, which have a very distinct Java flavor to them.
For example, I’ve had an idea for an Android app for a while now. Like most mobile apps, it would involve Internet connectivity. So I figured one of the most fundamental things possible, and a good way to get my feet wet, would be to write some code to perform a login operation. It happens that I’ve already got a simple DWS-based web server I can use for stuff like this, and an OUYA for testing, so I set up my network to make sure the devices could see each other and went to work.
The goal is to create a simple login function: take a URL as input, POST a hardcoded username and password to this URL that executes a login command on my server, and return a string containing the same HTML you would see if you performed the same login in a browser and then used the View Source command. As a baseline, here’s how I’d do this using only out-of-the-box libraries in Delphi:
function TMyObject.Login(const url: string): string;
params := TIdMultiPartFormDataStream.Create;
result := http.Post(URL, params);
TIdMultiPartFormDataStream is a bit obscure. If you don’t know anything about it, it might take a bit of time to research it. But once you do know about it, this only takes a few minutes to write, and it works. It’s very intuitive, and it works exactly as you’d expect, the first time. I’ve tested it. (I don’t have Android support for Delphi, but this works on Windows, and given the changes to the Indy source code shipped with XE5, I can only assume it works with Android too now.)
I tried to do the same thing with Oxygene for Java, using an Android template and actually running this on my OUYA. I immediately ran into a platform irritation: trying to run anything like this on the main thread will get your application killed. Apparently all network access has to be done in the background. So a bit of research turns up the AsyncTask class, which is how you run something in a background thread on an Android device.
AsyncTask is a mess and a half. It requires three generic type parameters, which can optionally be defined as Void if you’re not using the functionality involved. The documentation goes on and on about restrictions: you can only create or invoke one from the UI thread, in most (but not all!) Android versions, it will run all AsyncTask requests on a single background thread, and it must report back to the UI thread and not some other thread. You subclass it and override the virtual method that actually does the work you want, (much like TThread,) and then you can either call Get to wait on the work to be done, or override a second method to asynchronously report back to the main thread. (With a feature like that available, you’d kind of expect that you could use Oxygene’s async/await language feature here to simplify things, but it doesn’t work.)
So I override AsyncTask with a new class. I call it WebRequest:
WebRequest = class(android.os.AsyncTask<HttpPost, Void, String>)
method doInBackground(url: array of HttpPost): String; override;
Yeah, that’s right. The DoInBackground method actually takes an array of your basic input type, which in this instance is HttpPost. Unlike Indy, where you have a TIdHTTP class that handles all HTTP requests, the Android libraries have their own classes for each request type. Oh well. Different styles, I guess. But it gets better. Now you actually have to set up the POST request.
The HttpPost class has a method called SetParams, which takes a HttpParams object, which you add names and values to. Intuitively, you’d think that that’s how you set up a POST request with parameters. But when I tried that, the server didn’t receive any parameters. So I asked about it on StackOverflow, and found out that SetParams actually does nothing (it’s there because it’s part of a parent class) and the correct way to do it is to create a list of name/value pairs, wrap that in a UrlEncodedFormEntity object, and then call the HttpPost’s SetEntity method on that. Obviously!
So with that, I can fire off my POST request, and look at the string it retur–wait, no, it can’t be that simple. We’re in Java-land, remember? First, the HttpPost object can’t actually request anything; it’s just an object that describes the action to take. If you want to fire a HTTP request, you have to create a AndroidHttpClient object, which is analogous to TIdHttp except that it doesn’t have its own methods for HTTP requests; you feed it objects like the HttpPost instead. So I set one up and code up the request. It returns a string containing the results from my–oh, wait. Nevermind. We’re still in Java-land.
I can only assume that the folks who wrote all this Android code were part of the Chrome team at Google, because the whole thing appears to be written to benefit people who are writing web browsers, not attempting to simply retrieve results from servers. It returns a HttpResponse object, which contains a lot of metadata about the response, but has no method anywhere that returns a string! To actually get at the output, you need a BasicResponseHandler object, which knows how to magically extract the HTML returned by the server from the HttpResponse object and return it as a string.
So I finally have this to the point where it will compile and run, with all the Android permissions set correctly and the background code set up propertly. I build, deploy to my OUYA, and eagerly examine the output, hoping to bask in the sweet, sweet HTML I was expecting… and instead get a 302 redirect from the server.
You’ve gotta be freaking kidding me.
Indy gives me the HTML. It sees the redirect and handles it internally, like a Web browser would, by default, out of the box. I don’t have to care about redirects. I don’t have to know redirects exist. (I do, of course; I’m the one who set up the server page I’m calling into. But client-side, I shouldn’t have to.) But in the Java framework, it all gets thrown in my face. Time for some more research.
The research doesn’t actually reveal any clear way to get AndroidHttpClient to handle the redirect, so I end up doing it manually, which means I have to actually care about the HttpResponse object and not just hand it off to BasicResponseHandler, because it’s got the metadata I need. So I have it check for 301 or 302 redirect codes and fire off a GET request (which requires creating a new HttpGet object, of course) if it sees that.
I build, deploy, and run again, and finally, FINALLY there is HTML as the output, that actually came from the server.
It’s the source of the login page I just used, not the page I was supposed to be redirected to after logging in.
I poke around server-side a little and find out that dumped me back to the login page, as it was supposed to, because the second request did not contain the session cookie that the server issued as part of the response to the first request. Of course. (When I did this in Indy, I didn’t have to care about cookies or know they exist, any more than I had to care about redirects.)
Back to poking around in documentation. So apparently the right way to deal with this is to create a BasicCookieStore object and attach it to the BasicHttpContext object that your AndroidHttpClient is using for its request. I do that… and still get redirected back to the login page. For some strange reason, it’s not working. So I asked about that on StackOverflow… and got nothing.
This is the point where I gave up.
This took me two days to research, required me to write well over 100 lines of code, define 3 classes (not counting the main class that’s invoking all of this), use 12 Java framework classes, include 18 units in my USES clauses… and ended in failure. I have no doubt that actually making it work right would take even more code and even more complexity, all to do what I accomplished in a single, 14-line procedure in Delphi.
Here’s what I’ve concluded from all this:
First, I’m not sure how anyone ever manages to actually create an Android app, when even the most simple and fundamental of tasks require dealing with this much runaround.
Second, RemObjects already has an Oxygene native code compiler for mobile devices, for iOS. They really ought to make one that targets the Android NDK. (Maybe then I could use Indy!)
Alternatively, maybe they should figure out some way to use the NDK to host a CLR VM on an Android box, and then I could use the full Oxygene feature set. But just try Googling about ways to run .NET apps on an Android device and you’ll see that it’s something that a lot of people are trying and only accomplishing with very, very limited (and very expensive!) solutions, so this might be technically infeasible. But either way the current solution is no good, mostly because it’s chained to Java, which is no good. I ended up feeling like the guy who wonders why he took that Java pill in the first place, and if he had it to do all over, would have just chosen the blue pill instead.
Third, I’ve gained a tremendous amount of respect for Remy Lebeau and the rest of the Indy team for putting together a library that manages to do such a good job of dealing with this all complexity where it belongs–inside the library–and not tossing it in the developer’s lap!
As always, any feedback is welcome. I’m particularly interested in knowing if there’s any intuitive Android HTTP library available on par with Indy. Maybe I’m just looking in the wrong part of the system libraries?