A Topo map viewing application for the Android

April 9, 2013

Introduction

In March of 2013 I got serious about writing the topo map viewing application for my android that I had been talking about for two years.
See details here: This article discusses bug fixing, feature addition and other "maintenance" activities subsequent to development.

We keep going back to the Grand Canyon

For no particular reason, my application starts up with the maps centered on top of Horseshoe Mesa in the Grand Canyon, at the 24K scale. I have been finding that for reasons I could not explain, the application was continually returning to this starting location after displaying at a different location. What appears to be happening is that when the tablet changes orientation, the activity gets restarted from scratch. Not what I would have expected, but that is how Android does things: It is possible to screw with the android manifest and specify that you want to be notified of configuration changes, in which case you must respond to onConfigurationChanges(Configuration), but this is not the best approach.

The real fix is to expect a configuration change to cause the activity to pass through onPause(), onStop(), onDestroy() when a configuration change happens and to save state via saveInstanceState(Bundle), and then restore it again when the activity starts. Note that onCreate(Bundle) gets passed Bundle as an argument.

Setting up an onSaveInstanceState(Bundle state) callback and then processing the Bundle (which is null on initial startup) in the onCreate() callback fixes everything nicely.

Better handling of LruCache

The Google documentation on this is sketchy, but maybe you can piece together the details with some effort. I have tried adding the sizeOf and entryRemoved callbacks and just stirred up trouble so far, so have commented them out. Some things I should do, but don't yet: The Xoom has enough memory in "big heap" mode that I can get away with being sloppy, but eventually the map viewer will get an "out of memory exception" and die if I roam around a lot. Fixing the above would make things better (and make things more responsive when I fix the orientation change persistence thing).

It looks like there is an entryRemoved() method to LruCache that gets called when entries get bumped from the cache, but there are tricky issues involving using this that I don't understand fully yet, so I am avoiding tangling with this.

Another whole angle on all this is to keep the map cache in what in the Android world is known as a "content provider". This would be a separate activity with its own memory limits, and had already caught my attention as a way of circumventing the Google per-application memory limit (which has become less of a pressing need given the 256M limit I get on the Xoom via the large heap option). Even Google suggests using a content provider to handle the cache, which somewhat surprised me (to my way of thinking, a content provider would only make sense if more than one application were sharing the resource provided).

Android SDK version

I began using some API feature and found eclipse scolding me that it would not be present in the level 8 android API that I had specified at the minimum I wanted my application to be compliant with. This is an easily changed setting in the android manifest, and I bumped it up to 12 to get things on the air. However, this now means that my application won't run on stone age tablets and phones. Here is a quick breakdown of what these SDK level numbers mean as far as android releases: My Xoom is running 4.1.2 (Jellybean), so clearly level 12 is not at all unreasonable. If I was worried about marketing this application, I might have to think harder about this. The following link gives some statistics about how many devices out there are running what version of android: According to this, about 40 percent of android devices are running Gingerbread, which explains why the Eclipse default was level 8. I tend to think a lot of these are older phones, which are not the target for this application anyhow.

Fix the blue bar problem

The problem here is that as we move north or south in latitude, sometimes the width of maplets changes. And we have been assuming they do not and can just be drawn without scaling onto a grid determined by the size of the maplet containing the center point. The fix is to scale each maplet to fill the rectangle in the grid it is expected to fill.

I do this "brute force" by always setting a source and destination rectangle when I call drawBitmap(). There is every chance that drawBitmap() is clever enough to recognize when the rectangles are identical and to avoid needless computations. I don't know, and maybe should just assume so, but am tempted to check and make the simpler call to just translate the bitmap when that is all that is needed.

I might be even smarter instead of letting the central maplet define the scaling, to define the central scaling based on something absolute like degrees per screen pixel. Doing this would have a couple of advantages. One is that it would make it very easy to implement a variable scale slider or any kind of intermediate scales by just setting one variable. The other is that it would avoid what is happening now, namely that the map scale can change and the map (breathe) when the center point moves across a pixel size discontinuity. One of these is particularly obnoxious moving south of 31.75 north latitude right around the entrance to Madera Canyon. Letting the map set the scale south of this line makes the map look squeezed - it needs to be stretched. And the scale really needs to be set so that the X and Y scales match to avoid aspect ratio distortion.

Android exit on error

I would like to do this when I encounter ugly situations, but surprisingly it does not seem that a proper Android application should ever exit, or that is the party line.

The short answer is that you do this by calling the finish method in the main activity:

this.finish()
The longer answer drags you into all kinds of peculiar android design philosopy. People will argue that an application should never decide to exit or even offer a button to a user to allow them to exit. All this is well and good (maybe?), but the reason I would like to do this is because I have detected some kind of fatal error, probably due to a bug of my own, and would like to exit given some hopeless condition. I guess I can always divide by zero or keep a null pointer around handy to reference, but there should be a better way. Perhaps throwing an exception that I catch in the main activity and call finish()?
Feedback? Questions? Drop me a line!h

Gtopo / tom@mmto.org