I thought I would follow up my post mortem of the iPhone version of Prescriber’s Letter with a discussion about the complexities involved in porting the app to the Android OS. This will be a long blog post, so continue reading only if interested in the technical details involved with Android development.

If you haven’t already read the iPhone post mortem you should probably do that first as this post will largely be discussing the differences of the platforms and how I solved the Prescriber’s Letter (PRL) requirements on Android.

The first thing I did was hit the books. I purchased these e-books from Mark Murphy , and they proved invaluable. If you’ve asked a question on the Android beginners or Android developers Google groups then you know who Mark Murphy is, he seems to answer all of them. I read over all of the Android developer guide (glossing over stuff that would have saved me time if I’d read more thoroughly that’s for sure).

Activity Design

Most Android applications are centered on the concept of the Activity. An Activity is a class that is launched with an intent to display a GUI that allows the user to perform a task. It is the interface between your code and the OS (file resources, device information, network connectivity, really everything hardware goes through the Activity). When I first planned out how the app would work I went with a design that looked like this:

The original design had the “Main” activity (set as the default in the manifest for the app) launching the login screen, the login screen would do its work, launch the main menu screen and then invoke finish(). The main menu activity would then invoke all the sub-activities of the app (Issue screens, articles, detail documents, etc…). This original design was flawed in that it was ignorant of the life cycle of Android applications. When developing Android apps you need to plan for how your activities will react to being created, resumed, paused and stopped. You need to incorporate what will happen when the user presses the back button. If you’re used to iPhone programming you’re used to a home button press shutting your application down completely. On the Android hitting the back button, even on your root activity doesn’t shut anything down. The Android model wants all applications to be running all the time (in a perfect unlimited resources world). Your applications are only closed when the system determines it needs to free up resources for the current highest priority applications, and your apps are the lowest priority apps (because they’re in the background and not currently the focus of the user’s attention). So about midway into production I ended up having to do a lot of rearranging and revising my plan for the way activities were launched and how each activity would react when it was resumed. I came up with this design:

In this design the main activity is the root, and it launches either the login screen or the main menu depending on what state the application is in. When it launches activities it uses the startActivityForResult() method, and waits for a response. When the login screen finishes its work it will respond with instructions to the main activity to display the main menu. When the main menu finishes its work it will respond with instructions to load the login screen (and either start an automatic login, or just sit there). This system carried forward to the other activities as well, the main menu would launch sub activities and wait for responses (and sub activities would do the same). Each activity from the main menu and up could respond with several control instructions like “pop to the main menu”, or “Close everything down and go to the login screen”. This created a very flexible system that let me jump around as needed and fit the Android activity life-cycle much better.

Application Persistence Problem

The persistent nature of the app on Android presented a major problem to us. The app was designed on the iPhone to do a bunch of preliminary work at the beginning: authenticate the user, upload stored quiz results from when they answered quiz questions while offline, check for and download new content bundles and check the expiry date on the user’s account. Because we could no longer rely on the app having done all of these things before use we needed to come up with a plan to periodically perform these tasks. I wrote a class that fires up a thread that goes to sleep immediately and wakes up every few hours. When it wakes up it performs all the startup operations, and if it finds any issues that need to be addressed it sends a message to the current activity. The current activity can then make use of the Activity response system to tell the app how to handle the event. For example if a new content bundle becomes available, the updating thread will detect it, inform the activity and the activity will then ask the user if they want to download the new bundle. If they chose yes, the activity finishes with a response to drop all the way to the main activity and fire up the login screen with an automatic content download attempt. Very handy!

AsyncTask, it’s good, use it.

For all of the thread work I needed to write for the app I used the AsyncTask class rather than write my own thread. I am enjoying the philosophy of these new platforms to create wrappers for thread management. The iPhone has the same thing with the NSOperationQueue and NSOperation objects. AsyncTask takes the trouble out of communicating between the thread launching the task and the task itself. It also protects you from performing GUI operations outside of the main UI thread. All you do is figure out classes for parameters to the task, and return values from the task and then implement the doInBackground() and the various onPostExecute(), onProcessUpdate(), etc. methods. Execute the task from the main thread and away you go.

I thought I was leaking threads for the longest time while watching the debugger and when I finally went to address it, I found out by searching around that what actually is happening is that the AsyncTask class makes use of an internal pool of threads to do your jobs. They do stick around, but that’s intentional. Seems efficient to me too.

Porting Core Database Logic

Porting the core database logic was extremely straight forward. On the iPhone project we designed all of the database work so that it would be easy to port to Android or the Blackberry. All of the functions were straight C functions executing SQL with SQLite3. Since Android has an SQLite library porting all of this logic was no problem. There was a lot of code to port over and switch to Java, but no real complexity involved since the logic was most implemented via SQL.

SD Card vs. ROM Storage

When you’re storing data in an Android app you need to decide between storing on the built-in ROM or the mini-SD card most of the devices come with. The conventional theory is that if you have a lot of data you should store it on the SD card. The problem with storing data on the SD card though is that there is no security to it, and the data is not uninstalled when the app is uninstalled. These two points led us to prefer storing data on the ROM, but also writing code to use the SD card if there is insufficient space on the ROM.

ListViews

One pleasant surprise to me was the much easier way you create and manage ListView widgets on Android. By using the methods described in the Commonsware book I was usually able to get the data/view create logic up to speed quickly. Some were more complex than others but overall it went quite smoothly. For each screen that had a list I would write a class that extended BaseAdapter. By overriding the inherited methods from that and setting an instance of the class as the ListView’s adapter I had full control over what types of views would show up in the list, generating views for each cell, and configuring the contents of each cell’s view. From there it was a simple matter of inflating XML layouts into the cells on demand. This made things very flexible and adjustable at runtime, whereas I find the iPhone’s ListView not so flexible. The iPhone version really wants to have everything sorted out when the List is being created. It wants to know exactly how big every cell is going to be, what’s going to be in the cells, etc… Because you can inflate XML-based layouts in the Android version and those layouts can include flexible containers and widgets that adjust based on the content you put in them at runtime, everything sort of just works. I think that this obviously has some tradeoffs. Luckily our app doesn’t require any lists that have hundreds and hundreds of cells or I’m sure it would all come crashing down, in the end though for our app the experience is much better and the lists themselves look and feel better. Well, certainly they were a lot easier to code up.

Screen Resolution / Pixel Density / Orientation Issues

While ListView objects were easier to get right, I can’t say the same for the rest of the GUI. This project as a whole took a lot longer than it should have given that I had all the hard parts already figured out from the iPhone project. A big part of that reason was layout programming. Building GUIs by hand through XML is not my opinion of a pleasant way to spend several months. There’s a lot of guesswork involved, and the platform itself is still immature and luckily for you it’s your fun experience to find out where the holes are and work around them. For example, on one particular ListView we had a problem where the cells would not grow to wrap their content, but only when testing on Android 1.5 in the emulator. It took a long time to figure out that for some reason Android 1.5 just didn’t like the use of the RelativeLayout group in that scenario. When I changed things around to use a LinearLayout voila everything worked perfectly. This was not the only instance of this kind of craziness I got to enjoy throughout development. I spent a lot of time scouring posts on the Internet, tutorials, the Commonsware book, etc.. to navigate the quirks of laying out the GUI in such a way that it would work on every device for every version of the OS.

There are a ton of Android devices out there, their screen resolutions and pixel density are all over the map. When you decide to make an Android application this needs to be front and center in your design right from the very beginning. You have to make promises to yourself that you aren’t going to take any shortcuts in how you implement your layouts. No using AbsoluteLayout. No using fixed pixel dimensions or text sizes. It’s a very strange thing, but it has to be done. Basically you’re programming for the web, except some people are running your app on wrist watch sized screens, and some guys are running it on huge 480×854 resolution smart phones.

The majority of our layouts grow and stretch perfectly. Midway through I switched over to using “Xdp” measurements for any padding or margins for GUI components and that took care of the pixel density issues. The login screen was the biggest hassle because it has the most text and controls to interact with of all the other screens. The other screens tended to be large fullscreen controls that just got smaller or bigger. For the login screen I had to even go so far as to define an alternate layout file for landscape.

The IDE, debugger, Emulator and Build Environment

This by far was the most frustrating part of the Android port experience. I found the Eclipse IDE extremely slow, buggy and mainly just a general pain in the butt. The emulators supplied with the Android SDK are slow and unresponsive. The build system is a pain as well but that’s Java’s fault not Eclipse or Android. I have yet to determine where the performance fault lies, whether it’s my Macbook Pro with 4GB of RAM, my Android Dev Phone 2 or just the way Eclipse and the Android tools were written. I could never leave Eclipse running for long periods of time. If I left it running overnight I would come back to an extremely slow program that would take 15 – 20 seconds to switch between files, or up to 2 minutes to rebuild the workspace when I save a layout file. I’d investigate and see that Eclipse was operating with a 6GB pagefile on my machine. Rebuilding the workspace constantly was fine some of the time, but really a pain when you forget that it’s turned on. If you want to make a few small changes to a bunch of layout files you have to be smart and save them all at once when you’re done or else if you save them one at a time you have to wait for the build to complete each time. It was the culmination of these points listed and another million little things that just made dealing with the IDE and the Android tools just a general poor experience on a daily basis.

Towards the end of the project I had to tackle the task of turning the generic code base into 5 different apps, each targeting the 5 different publications offered by my client (Therapeutic Research Center). On the iPhone this was a very simple process, I used #defines to section off any platform specific code, and I made use of the target system for defining certain resources as belonging to one publication or another. To build all of the apps it was a simple matter of a making a group target that depended on each publication target. Since Java does not have a precompiler, and due to the way the R resource class is built and an integral part of everything you do on the Android this old way of doing things wasn’t going to fly.

I ended up having to use the method described by all of the developers on the Google groups who’ve attempted to make “lite” versions of their apps. Here’s the steps I followed:

  • I used the command-line project building tool to generate an empty project.
  • I took all of the build files out of that project in order to get a base set of build files (local.properties, build.properties, default.properties and the build.xml file).
  • I placed the build files into my project’s folder and then renamed them all except for default.properties. I fixed all the references to refer to the new names.
  • I then wrote a large Ant script that would essentially build one project at a time:
    • First it built the default project that I used all throughout development (targeting Prescriber’s Letter).
    • It then renamed the src and res folders to src_orig and res_orig, as well as the manifest.
    • For each publication after that:
      • I would copy all the files from src_orig to src, and res_orig to res.
      • I’d iterate through every java file under src, and every XML file under res and replace the package for the base app (trc.prl) with the current publication (trc.pl, trc.plc, trc.prlc, trc.ptl).
      • I’d then do the same thing in the manifest file.
      • Once the replacements were made I could then execute a normal build, so I’d do an ant call out to the other normal build file and it would build the existing state of the project folder as if the project had been developed that way to begin with.

At the end of the script all the original stuff gets put back where it was and the copies are all cleaned up. Basically everything above is a summary of what was generally what you would do by hand if you had a set of source code and you were changing it from one app to another but you wanted all the code to pretty much stay the same. The way I get around the apps performing differently was I had a single constant in a singleton class that also was string replaced to be one value or another. The code then references that constant to decide which assets to load (for background images, or string files), and which publication URLs to target for downloading content, authenticating and uploading quiz results. This process worked very well in the end. I even upgraded the script to do my entire build process for me. It signs the final apps, zipaligns them and will even install them on the current device if you call the install target. This process turned out quite well but it took a couple of days to implement, I’d trade it all in an instant for #ifdef.

Summary

Well, all complaining aside I can’t say that working with Android was all horrible. Some things were nice, and overall it was fun to learn yet another platform and all the complexities involved. I must say that I’ve become quite the expert at jumping into new platforms. The more I do it the more I see all the similarities an it becomes just a matter of “How did they solve this problem that always comes up, oh they went this direction, or that direction, ok I know how that works.” It is definitely getting easier that’s for sure.

The following are some screenshots from the end Android product. As you can see we strived to have the application look identical to the iPhone version as much as possible.

Main Menu:

Issue:

Article:

Share:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Slashdot
  • TwitThis
 

Leave a Reply