OHMS BLOG

Wednesday, October 26, 2011

code

Adventures in Android, Part III

Today I want to talk about some architectural decisions that I made in the early stages of development that I hope will help to minimize the memory and battery consumption of my app.

First, let's discuss how I intended for my app to receive input. I do plan on having to interface directly with the user, so I'll be implementing a few activities. I also have a couple of BroadcastReceivers that I have added to my app's manifest in order to receive certain broadcast intents from the OS.

It very quickly became clear that I was going to want to implement a service to do my app's heavy lifting. Since my BroadcastReceiver was going to need to send intents over to the service via Context.startService(), I settled on using an IntentService so that my service would automatically stop itself when there were no more pending intents.

I also decided that it was going to be necessary for my service to communicate back to my activities. I decided that my activities will use Context.bindService() to facilitate this bi-directional communication. On the other hand, I don't want the service to be bound any longer than it reasonably needs to be.

Observe this snippet from the Android documentation on the service lifecycle:

A service can be both started and have connections bound to it. In such a case, the system will keep the service running as long as either it is started or there are one or more connections to it with the Context.BIND_AUTO_CREATE flag. Once neither of these situations hold, the service's onDestroy() method is called and the service is effectively terminated.

This information is important because it clarifies what happens if an IntentService tries to stop itself while the service is still bound: The service won't be destroyed until any connections are unbound.

After digesting all of this for a few moments, I came to the following conclusions:

  • My service will be implemented as an IntentService and it will process start intents sent by my activities and broadcast receivers. Unless any connections are bound to it, the service will stop itself as soon as it has processed all the intents in its queue.
  • My service will also support having connections bound to it. My activities will bind in order to achieve bi-directional communication with my service. Because of the guarantees made in the Android service lifecycle documentation, the IntentService implementation can't "pull the rug out" from under bound connections.
  • I will under no circumstances bind anything to the service unless the communication is essential to the functioning of my app. In other words, I'm not going to bind to the service and leave it running "just in case." In the case of my main activity, this means that I'll bind to the service in Activity.onStart() and unbind in Activity.onStop(); if the activity isn't visible and there aren't any start intents pending, then my service doesn't need to be consuming resources.
  • To compensate for the fact that the service is unlikely to be running continuously, I ensure that my activities send a refresh command to the service when they become visible. This gives the service an opportunity to update the activity as necessary to ensure that the UI is consistent with the service's internal state.

Monday, October 24, 2011

code

Adventures in Android, Part II

A brief annoyance that I was dealing with:

For testing purposes I wanted a service to initiate a status bar notification. I couldn't understand why, but for some bizarre reason Android kept failing miserably when trying to load the resource for my notification's icon. I kept seeing stuff in my ddms logs about android.content.res.Resources$NotFoundException.

I checked my code and my resources over and over again. I blew away the gen directory and rebuilt my app from scratch. I scoured StackOverflow for a situation similar to mine, yet nothing quite the same came up.

After pulling my hair for an hour or two, I inadvertently realized what the problem was.

I'm running CyanogenMod 7.1 on my phone. I had it set to forcibly install apps to my phone's SD card. Unfortunately for me, my app doesn't include any SD card support whatsoever.

*facepalm*

Problem solved: I moved the app into my phone's internal memory. I also decided to end my little experiment with forcing new installs onto the SD card.

Bloody hell!

Sunday, October 02, 2011

code

Adventures in Android, Part I

I've spent most of this afternoon and evening working on my first Android app.

I was dissatisfied with the current slate of applications that are available for a particular problem domain, so I have decided to write my own.

More details will follow as my work approaches completion, but in the meantime I'm going to jot down some thoughts about the stuff that I worked on today.

  • I haven't written a line of Java in probably seven or eight years because I think that it sucks. It wasn't difficult to get back into it, though I still don't like it.
  • I hate checked exceptions; they make the compiler annoying enough to motivate you to write suppressing catch blocks just so that your code will compile (Doing so is a bad idea, of course, so suck it up and do the right thing).
  • The APIs I wanted to use were not available publicly until Honeycomb. I want my app to run on Eclair. I ended up using reflection to give me the appropriate interfaces depending on availability: I use the documented API if it's available, but if I catch a NoSuchFieldException then I fall back to the undocumented APIs. I am amused that my first Android app, the one that I am writing to teach myself, is using undocumented APIs; I did the same thing when I started hacking 16-bit Windows code in 1995. It's actually a bit easier this time around because I may browse the entire source code. When I taught myself Windows I didn't have that luxury, though I always had my trusty copy of Undocumented Windows sitting nearby (I still have that book)!
  • I haven't used Eclipse since the last time that I wrote code in Java. This time around, I am proud to say that I successfully resisted using Eclipse for my Android work. I was worried that command-line environments were going to be an afterthought when it came to the Android developer tools but I was pleasantly surprised to be proven wrong. Vim and Ant FTW!
  • ddms is really cool.
  • Today's work passed testing! svn commit

<digression>This experience reinforces my opinion that hiring developers based on their ability to satisfy a laundry list of keywords is ridiculous. Good developers don't need a year to pick up whatever technologie du jour you're pushing. Whatever your buzzwords are, they'll probably be obsolete in five years anyway.</digression> That's another topic for another day.

Release 7.0; Copyright © 1996-2012 Aaron Klotz. All Rights Reserved.