Sunday, February 7, 2010

 

Android vs. iPhone: A Developer's Perspective, part I

Android vs. iPhone: A Programmer's Perspective


(See also Part II.)

I've spent the last month or so building both Android and iPhone versions of my app iTravel. (See http://wetravelright.com/ for details and links.) Which gives me a pretty good perspective from which to compare and contrast the Android and iPhone environments and SDKs. Hence I give you the following head-to-head analysis, from a developer's point of view:


Language

Non-programmers often think that one's language of choice is a big deal, but really, once you've learned two or three programming languages, picking up another is generally something you can do in a day or two. That said, there are often substantive differences. And this is definitely true of Java (for Android) and Objective-C (for iPhone.)

Let's start off with the really annoying stuff: Objective-C's memory management. By which I mean, its complete lack of any. Programmers have to manually allocate and release memory when writing for the iPhone SDK, which is positively medieval. If you fail to do so, and there are many pitfalls, then you leak memory which is lost until the device reboots. This is awful. (And it's no longer true of the Mac SDK, incidentally; but the iPhone is behind the times.)

There are other annoyances. You have two files to contend with for every class - a .h and a .c file. Which is inconvenient and complicating and inelegant. And suppose you have a basic, bog-standard instance variable. You generally wind up declaring it in, count 'em, not one, not two, not three, but four different places.

In your .h:

{
NSObject *object
}

@property (nonatomic, retain) NSObject *object


In your .c;

@synthesize object

and later, in dealloc(),

[object release];

Whereas with Java, you have one single .java file, which in general will have

private Object object;
public Object getObject() { return object; }
private void setObject(Object o) { object=o; }

all in one place. I know which one I think is easier.

But. On the other hand. Objective-C is sort of the bastard son of C, which is awful, and Smalltalk, which is awesome. As a result, it has Smalltalk-like features like selectors:

[caller performSelector:@selector(myFunctionName)]

...in short, you can use functions as (more or less, ish) first-class objects. Try that in Java and if memory serves you'll most likely wind up in the irritating labyrinth of reflection. Plus, you get options like "doesNotRecognizeSelector:", which can be easily misused, but is potentially very powerful, and does not exist in Java.

On the other hand, Objective C is really annoyingly logorrheic (meaning wordy) especially when it comes to string handlers. Suppose you have strings A and B, and you wish to combine them into string C. In Java, the syntax is

C=A+B;

whereas in Objective-C, you type

C = [A stringByAppendingString: B]

Or suppose you want the location of the last slash in string A. Java:

n=A.lastIndexOf("/");

Objective-C:

n = [A range ofString:@"/" options:NSBackwardSearch].location;

I much prefer Java's syntax and simplicity. But I do admire Objective-C's flexibility.


Development environment

By this I mean: the "integrated development environment" in which coders work; the documentation for the IDE, the language, and the libraries; the testing and source-code support; and all the stuff that is meant to help you write better code faster.

The Android and iPhone IDEs and documentation really incarnate the attitudes of the two companies in question. Android doesn't exactly have an IDE of its own, although they recommend that you use the Android plug-in for the open-source Eclipse IDE, which is slow, irritating, and buggy in various minor ways. The documentation is written by smart people for smart people, with little handholding. Flashy graphics are minimal to nonexistent. And a lot of important stuff is still handled by tools meant to be run from a shell rather than a GUI. But the search function is excellent.

Apple's IDE is slick, seamless and powerful. It comes with a visual tool to help you lay out the screens of your app. The documentation is full of step-by-step guides (although they are often oddly lacking or confusing) and high-quality graphics and other visualizations.

I have many complaints about both. Eclipse is slow and annoying, and I could only get Android's JUnit test harness to run successfully from a terminal window, rather than the IDE; similarly, I had to use shell tools to sign packages, get a fingerprint for a Maps IDE, install a package on my phone, etc. All of which really calls into question the I in IDE.

On the other hand, at last the external unit-testing actually works; XCode's built-in harness is so bad that Google built and released an entirely separate one, which has the advantage of actually functioning correctly. On the other hand, its Subversion integration is excellent, which is not true of Eclipse.

Both have plenty of official and unofficial online support, as well, at official support sites and places like Stack Overflow. Android's open-source ethos gives it a big advantage in terms of external packages, though; for instance, if you want to build a barcode scanner into an Android app, there's a whole open-source library out there for you, ready to be plugged in. For the iPhone? You'll have to roll your own. Sorry.


Multithreading

In phone apps multithreading is key, because the phone needs to remain as responsive as possible, so you need to do your heavy lifting behind the scenes, outside the main UI thread.

Both the iPhone SDK and Android support multithreading, but the latter's is much more convenient, especially if you want to call back to the main thread once your background thread has done its thing. On the iPhone, you have to do something like:

-(void) doWebView {
NSThread* htmlThread = [[NSThread alloc] initWithTarget:self selector:@selector(loadHtml) object:nil];
[htmlThread start];
[htmlThread release];
}

-(void) loadHtml {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *html = doUnroll ? [self getUnrolledData] : root.data;
html=[NSString stringWithFormat: @"%@%@</body></html>", [Util getBaseHtml], html];
[self performSelectorOnMainThread:@selector(setHtml:) withObject:html waitUntilDone:NO];
[pool release];
}

-(void) setHtml:(NSString*)html {
[webView loadHTMLString:html baseURL:nil];
[webView sizeToFit];
[self.tableView setTableHeaderView:webView];
}

Whereas on Android, you can use Java's equally irritating Runnable() framework, but Android provides an extremely convenient (and quite flexible) short form. Just subclass AsyncTask in an inner class:

new LoadHtmlTask().execute();

class LoadHtmlTask extends AsyncTask {
protected String doInBackground(String... strings) {
String headerData = Settings.GetHtmlPrefix() + (unrolled ? getUnrolledData(viewRoot) : viewRoot.getData());
return headerData;
}

protected void onPostExecute(String results) {
mHeaderView.loadDataWithBaseURL("local", headerData, "text/html", "utf-8", "");
}
}

which seems much more encapsulated and intuitive to me.


Persistence

On paper, the iPhone environment has a big advantage here: it features the Core Data object-persistence layer above the SQLite database, whereas Android requires direct DB access.

For me, though, direct DB access was not awful, and given the constraints of my app, arguably simpler than jumping through all of Core Data's hoops - using the separate tool to declare a data model, having to rebuild your custom code every time you add a column, etc. All that without even getting thread safety.

However, I'm totally comfortable writing SQL, not all developers are, and my app had pretty straightforward DB requirements. Core Data is undeniably more elegant and ultimately better. Plus you get nifty little features like shake-to-undo, semi-automatic migration data models in installed apps, etc. And it's not like the Android tools are particularly easy to work with. Check out the method in android.database.sqlite.SQLiteDatabase you use to perform a query:

public Cursor query (boolean distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit)

As a (Google employee) friend of mine said, "Holy positional parameters, Batman!" Needless to say, one quickly wraps this monstrosity in other methods less prone to grievous error...


Part II of this post compares and contrasts system features, phone features, settings and resources, screen building, internet connectivity, and the app install/release process. (It does not compare and contrast graphics programming, as my apps are data-heavy not graphics-heavy.) Don't touch that dial.

Labels: , , , , , , , , , , , , ,


Sunday, January 31, 2010

 

Back in the Android saddle

So now that versions 2.0 of my iPhone app are up and running on the App Store, I've finally started writing an Android version. You may recall that this blog was supposed to be primarily about Android development from the get-go. We apologize for the eight-month sidetrack.

Anyway, It's been going quite well. Both the Android and iPhone SDK have their pros and cons. I like XCode better than Eclipse, and I generally (though not always) prefer Core Data to direct DB queries; on the other hand, string processing is far easier in Java, Android's multithreading is easier to work with, I vastly prefer single .java files vs twinned .h/.c files, and I love not having to worry about manual memory management.

The Android version is now mostly functionally complete. Things I have (sometimes re) learned which may be of interest:



More to come when I release v1.0, which should happen in the next ten days.

Labels: , , , , , , , ,


Sunday, May 24, 2009

 

Background tasks and upfront dialogs

Android's process and threading model is fairly simple. Your application runs as a single process, but can spawn multiple threads, and is strongly encouraged to do so rather than block UI while waiting for, say, an upload or download to complete. (In fact, if you hang the main UI thread for to long, the OS will terminate your app.) You can do this one of two ways:



Here's the AsyncTask I wrote today:


private class UploadNoteTask extends AsyncTask {

protected Boolean doInBackground(Long... longs) {

//create progress dialog
publishProgress(1);

//we can't do this in the target because it's not an Activity
SharedPreferences mySettings = getSharedPreferences(Util.AppName, 0);
String currentEmail = mySettings.getString(Settings.KEY_EMAIL, "");

//try to upload the note; longs[0] is the row ID
boolean noteUploaded = mDbHelper.uploadNote(longs[0], currentEmail);

publishProgress(100);

return new Boolean(noteUploaded);
}

protected void onProgressUpdate(Integer... progress) {
showProgress("Uploading...", "Uploading data", progress[0]);
}

protected void onPostExecute(Boolean wasSuccessful) {
//get rid of the progress bar
killProgress();

//tell the user how it went
if (wasSuccessful.booleanValue()) {
showDialog("Success", "Note uploaded");
}
else { // warn user of failure
showDialog("Failure", "Could not open note: error message "
+mDbHelper.getUploadError()
+". Try again later.");
}
}
}


(Yes, obviously I'm going to move all those hardcoded strings into R.strings very soon, improve the error handling, etc.)

And here's how you call it:
new UploadNoteTask().execute(mRowId);

which I think we can all agree is not too onerous.

I have, however, run into an annoying problem. It's convenient to run this as an inner class in your Activity, as you can then call Activity-inherited methods such as the "getSharedPreferences()" method up above. However, I want to be able to upload a note both from the list of notes and the detail page for a particular note - which means I have to either



So for the moment I'm holding my nose and duplicating code. (I can't put it in a common superclass because one of my Activities inherits from ListActivity, and the other does not.) Not just the class code, either, but the "showDialog" "showProgress" and "killProgress" methods, which tell the user what's happening/happened as you upload.

At least these kinds of pop-up dialogs are very easy to work with. Here are those three methods just cited, in their entirety:


private void showDialog(String title, String message) {
AlertDialog ad = new AlertDialog.Builder(this).create();
ad.setButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
return;
} });
ad.setTitle(title);
ad.setMessage(message);
ad.show();
}

private void showProgress(String title, String message, int progress) {
if (mProgress==null) {
mProgress=ProgressDialog.show(this, title, message);
}
mProgress.setProgress(progress);
}

private void killProgress() {
if (mProgress!=null) {
mProgress.dismiss();
mProgress=null;
}
}


Still, my cavil above aside, I must admit it's a nice and easy way to handle background processing in a separate thread, while keeping the user semi-informed.

Labels: , , , ,


This page is powered by Blogger. Isn't yours?

Subscribe to Posts [Atom]