Wednesday, December 18, 2013

Who Goes First App

Continuing with my series of boardgame-related side projects, I've written a small holo-themed app for determining who goes first called (unsurprisingly) Who Goes First.  As with most side projects it's also open source.  I released this app a while ago but thought I'd mention it here.

There wasn't much complex about it; mostly I was pleased with how easy it is to make a good-looking app with holo theming (even if you're not very talented in the design department).

The only interesting thing I learned relate to the arrow pointing towards who goes first.  If you need more than six arrows, then it'll just show "X#" inside of the arrow instead.  This required scaling the text size depending on how long the text was.  I ended up using a Region for the arrow (defined via the Path used to construct the arrow) and another Region for the text and test if they overlapped; if they did, shrink text size until they don't.  You can check out what I'm talking about in the actual ArrowView.

The one problem I've run into is when there are multiple people at the table with this app.  The app does not use networking to sync their random seeds, so you can end up with multiple people going first.  I guess my next project will have to be a "Who Runs Who Goes First First" app.  :)

Monday, December 16, 2013

Testing on Android (Part 1): Unit Tests

I've recently been doing research on Android testing.  I'm quite new to this business; automated testing has (for the longest time) felt unnecessary.  But I've been slowly convinced that the benefits outweigh the time costs - as long as you do it in an efficient manner.  To that end, I set out to evaluate the many, many Android testing frameworks to see which ones seemed to save the most time.

I began by summarizing all the tools/services I could find in a spreadsheet (let me know if I'm missing anything).  Beyond that, I am going to do a series of posts going into more detail about different parts of Android testing:
Let's start with unit testing.

Unit Tests

There's no reason you can't use normal JUnit 4 testing for Android applications... as long as you stay away from anything Android.

Normally you compile against the SDK's android.jar, which contains nothing but stubbed methods that throw exceptions when run.  When you actually upload your APK to a device, it uses the device's implementations of all those stubs.  As a result, when running normal unit tests in your IDE, you get no access to those framework implementations (instead receiving mountains of exceptions).  This is not a big deal if you're testing some simple functionality that doesn't touch Android itself.

Pros:
  • Fast and easy
Cons:
  • Cannot use any Android framework classes
The Android Testing Framework

The Android testing framework is the official method of unit testing on Android.  It loads your application onto a device, then runs JUnit-based test suites.  Since it runs on the actual OS you can use the Android framework as you normally would in your application and can conduct a series of realistic tests that way.

Ostensibly the testing framework is unit testing, but the slowness of having to fully compile and upload your app onto a device before executing any tests makes testing slow.  Plus, you have to make sure you've got a device attached or an emulator running.  As a result, I might consider the testing framework for semi-regular tests (e.g., whenever you push a new commit, or nightly tests) but I would have trouble using them while actively developing.

Pros:
  • Access to the Android framework
Cons:
  • Slow
  • Requires attached device or running emulator
  • Uses JUnit 3 (instead of the newer JUnit 4)
Robolectric

Robolectric is a project that unifies the speed of unit testing with the ability to access the Android framework.  It does this by implementing all those stubs with mocked classes.

Having tried it out, it is lightning fast and works as expected.  I'm also impressed with the amount of active development on it - this is a rapidly improving framework.  However, the active development does take a toll; documentation is a bit lacking, plus some new versions of Robolectric break things in previous versions.  Plus, it can't mock everything - for example, inter-app communication - since it's not on an actual Android OS.  That said, the benefits here far outweigh the negatives when it comes to unit testing Android.

Pros:
  • Fast
  • Can access mocked Android framework
  • Actively developed
Cons:
  • Not the true Android framework
  • Not everything is mocked
  • Lacking documentation
Conclusion

I'm a fan of what Jason Sankey said on StackOverflow about tiered unit testing: Prefer pure unit tests, then Robolectric, then the Android testing framework.  The tests get harder/slower the higher the tier, but sometimes you need it.

Thursday, December 5, 2013

Thoughts on the Current State of Android IDEs

I've repeatedly been getting the question "which IDE should I use for Android?" recently so I'm writing up a brief summary.  Here's my current thoughts on the state of Android IDEs (circa December 2013):

Eclipse + ADT

The original Android IDE, officially supported by Google.

  • Supports the most features for creating/building Android apps.
  • Abundant documentation, both from Google and from years of people asking questions about it on StackOverflow.
  • Many 3rd party tools, since it's been the main player for years.
  • Relatively stable; your project won't suddenly stop building when you update your sources.

IntelliJ IDEA + Android Plugin

A Java IDE that has an Android plugin built for it by IntelliJ.  This is the one IDE I haven't used extensively, so take my comments with a grain of salt.

  • A fan favorite; people rave about IntelliJ (especially those who were burned in one way or another by Eclipse).
  • As stable (if not more so) than Eclipse, because it lacks some of the built-in instabilities in Eclipse (aka, randomly crashing every once in a while).
  • Does not support NDK.

Android Studio + Gradle

Android Studio is a fork of IntelliJ IDEA with gradle-based build support.

  • Gradle is the future of Android build processes, so using it you'll be one step ahead of the curve.
  • Since it's still heavily in development Android Studio is quite unstable.  It's gone from rarely working to only stabbing you in the back every once in a while, but use of it requires extra maintenance work.
  • Does not support all features, like NDK or lint.  (There are workarounds, though.)
  • Improves weekly because that's about how often they push out updates.  If you love cutting edge technology that's a pro, if you like stability that's a con.  (You can switch off of the "canary" update channel if you're more conservative.)
Conclusion

  • If you need all Android features (like NDK support), use Eclipse.
  • If you hate Eclipse, use IntelliJ IDEA.
  • If you love gradle builds or want to plan for the future and are willing to put in some extra effort, use Android Studio.
Also, if you've got anything to add let me know - I'd be curious if there's some important pieces I'm leaving out, as I am far from an IDE expert.

Tuesday, December 3, 2013

Streaming JSON Parsing Performance Test: Jackson vs. GSON

A few years ago, when speed and memory became a concern in one of my Android apps, I implemented some streaming JSON parsing using Jackson.  At the time I chose Jackson over Gson because the benchmarks seemed to show it was significantly faster*; however, I've heard that recent versions of Gson rival Jackson for speed.  I want to re-examine the benchmarks again to make sure I'm choosing the right library.

The best benchmark I found is at json-benchmark.  There were a number of other benchmarks out there but they didn't appeal to me for various reasons, either because they didn't provide source, the benchmark didn't seem very fair, or it didn't test on the Dalvik VM.

Unfortunately, json-benchmark's last documented test is from two and a half years ago.  I decided to try to reproduce these tests against the latest libraries, Jackson 2.3 and Gson 2.2.4.  There were a few trials getting the test to run again:
  • The JSON benchmarking code has moved to libcore, so I checked out and referenced the latest benchmarks from there.
  • The ParseBenchmarks code to test Jackson and GSON directly was removed, so I had to re-add it (using the same old code, but with some package names updated).
  • vogar uses old assumptions about where "dx" (and related tools) are located; it still thinks they are in $ANDROID_HOME/platform-tools.  Rather than fix and recompile I just made my files match its assumptions for a bit.
Once I got it running, I ran the tests on a Nexus 7 (2nd generation), running Android 4.4.  Here's the results:

        document            api     ms linear runtime
          TWEETS ANDROID_STREAM  19.82 ===
          TWEETS JACKSON_STREAM  11.47 ==
          TWEETS    GSON_STREAM  17.98 ===
          TWEETS       GSON_DOM  39.00 =======
          TWEETS       ORG_JSON  30.86 =====
          TWEETS       XML_PULL  26.08 ====
          TWEETS        XML_DOM  85.32 ===============
          TWEETS        XML_SAX  33.83 ======
    READER_SHORT ANDROID_STREAM   4.60 =
    READER_SHORT JACKSON_STREAM   2.99 =
    READER_SHORT    GSON_STREAM   4.36 =
    READER_SHORT       GSON_DOM   8.07 =
    READER_SHORT       ORG_JSON   6.42 =
    READER_SHORT       XML_PULL   6.75 =
    READER_SHORT        XML_DOM  14.84 ==
    READER_SHORT        XML_SAX   5.54 =
     READER_LONG ANDROID_STREAM  38.24 =======
     READER_LONG JACKSON_STREAM  18.12 ===
     READER_LONG    GSON_STREAM  43.81 ========
     READER_LONG       GSON_DOM  72.34 =============
     READER_LONG       ORG_JSON  52.47 =========
     READER_LONG       XML_PULL  65.53 ============
     READER_LONG        XML_DOM 160.02 ==============================
     READER_LONG        XML_SAX  47.37 ========

Of course your mileage may vary depending on what exactly you're parsing, but with somewhere between 1.5-2x the speed I think I'll stick Jackson when I need my JSON parsing to be fast.

* Speed isn't everything; if Gson was only marginally slower than Jackson I still would have preferred it; I find their streaming parsing paradigm easier to use.

Monday, November 25, 2013

Sample Android Project: Movies

There's a sample application that I worked on recently with talented designer Chris Arvin for a talk we gave at DroidCon UK 2013.  I also repurposed part of it for the talk about animations I gave at AnDevCon a few weeks later.

The app is open source and you can check it out here: https://github.com/dlew/android-movies-demo

You can get the APK here: https://github.com/dlew/android-movies-demo/releases/



The interactions required a bit of interesting engineering so I wanted to discuss some of it here.

ViewPager and Decor

One of the key interactions we wanted was the app to feel like a ViewPager so that it was an interaction users were familiar with except that the result isn't quite the same - instead of paging, content would slide out from underneath other content.  This is not something ViewPager is inherently designed to do.

There were two options open to me.  One was to rip out all of ViewPager's event handling code and make my own custom View, which was not an appealing prospect.  The other was to find some way to manipulate ViewPager itself.

It turns out there is an interface ViewPager finds special called Decor.  If a View implements Decor, then ViewPager treats it as a special View that can remain on top of it the ViewPager.  This is normally for implementing your own tabs, but in this case I imported my own copy of ViewPager (since Decor is hidden normally) and made my entire UI a Decor View.

It's a neat trick that would work for any app that wants ViewPager event handling without paging Views, though I think if you wanted a less hacky solution you'd write your own event code.

Custom Views Everywhere

I've gone from avoiding custom Views like the plague to fully embracing them.

The key realization is that custom Views do not need to handle every situation.  If you're looking at framework custom Views as reference you will be overwhelmed quickly.  For example, during measurement framework Views have to handle WRAP_CONTENT, MATCH_PARENT, and everything in between; but if you know your View is always going to be MATCH_PARENT you can greatly simplify all your code.

The movies sample app is a pile of custom Views.  First there's SlidingRevealViewGroup, which generically shows one View slide out from another.  On top of that is built MovieRowView, which has the specific Views that we want to display.  Then there's SlidingPairView, which is a set of two SlidingRevealViewGroups that creates the side-by-side effect seen in the app.

I also needed a few other custom Views to shore up some other issues.  CenteringRelativeLayout just adjusts the film cover so that, as it shrinks in size, it still looks centered.  SlidingListView was required for performance; we needed to manipulate the rows directly when sliding, instead of constantly notifying of data changed.

Performance Tricks

The coolest part of the whole app is the slide: how the cover becomes smaller and the content slides out from underneath.  All of this was achieved through translation of Views.  I took advantage of the fact that ViewGroups don't render their children outside of their own bounds.  By translating content I could hide them outside the clip bounds.

When a slide starts, it throws practically everything into hardware layers then just slides Views left/right.  Moving around pre-rendered content is fast and as a result the paging is silky smooth.

Nowadays I'm a huge fan of those basic properties of Views (translation, scale, rotation and alpha).  They're the core reason to support ICS+ only; with these properties you can take your interactions to the next level.

Drawbacks

The drawback of the solution I came up with is that it creates a ton of overdraw.  This hurts performance, especially (as far as I can tell) when rendering a new row while scrolling up/down.  I sacrificed scrolling performance for paging performance.  Perhaps there is a way to achieve both, but I'm pretty much done with this sample for now.

The rounded corners could've been implemented in a much better fashion than an overlaid Drawable, but with the limited time before the presentation I had, I could not come up with a better solution.

Thursday, November 21, 2013

Is findViewById() Slow?

One of the presenters at Droidcon UK 2013 made the point that View.findViewById() has been unjustly villainized and that the ViewHolder pattern recommended by Google is unnecessary.  After hearing that I studied the code (in View and ViewGroup) and did not observe any heinous performance issues contained therein.  It's a pretty simple recursive lookup that walks the View hierarchy for an id.

As this article points out, findViewById() is necessarily slower than ViewHolder because it's O(n) vs. O(1).  But that doesn't mean findViewById() is slow.  If the operation is sufficiently fast and n is low then it doesn't matter if it's O(n).  Who cares if I have to do a 1 nanosecond operation a thousand times?

My hypothesis is that findViewById() is fast enough as to be negligible.  With that in mind, I cooked up a sample application that tests findViewById(): https://github.com/dlew/android-findviewbyid


You can dynamically create a hierarchy of Views, either adding to the depth or the number of children at each node.  When you hit "run" it searches for the furthest away id a number of times, then averages the time taken.  I ran the test on my Nexus 1, to somewhat recreate the situation described when Google explicitly recommended the ViewHolder pattern three years ago.

Here's the results from a test of many possible depth/views per node values: http://goo.gl/dK2vo3


As  can be seen from the chart, the time taken to use findViewById() is roughly linear with the number of Views in the hierarchy.  What's key here, though, is that for a low number of Views it barely takes any time at all - usually a matter of several microseconds.  And remember, this is on a rather old phone, the Nexus 1.

That said, if you were to call findViewById() many, many times it could cause a problem.  Admittedly this part of the post is going to be a bit more hand-wavy than the rest, but: I tried out scrolling on a ListView on my Nexus 1 - there is an upper bound to how fast I can scroll and I can't seem to break 60 getViews() per second.  That means that getView() is called once per 16ms frame.  Given that we're measuring findViewById() in microseconds, even calling it a dozen times (with a reasonable number of Views) shouldn't cause performance issues.

So my conclusion is that findViewById() is harmless in most practical circumstances.  I'll probably still use ViewHolder to avoid casting Views all the time, but performance will not be the main purpose anymore.

Disclaimer: I'm not a performance expert so I suspect there's something I'm missing (especially since I'm going directly against Google advice here).  If someone knows of a missing link let me know!

Monday, November 18, 2013

Android Talk Slides from 2013

I'm done with talk season!  In case you missed them, here's the slides:

Crafting Unique, Delightful Apps

I Can Animate and So Can You

I suggest downloading the Keynote files for the presentations.  They animate!

Wednesday, October 23, 2013

DateUtils.getRelativeTimeSpanString() and Timezones

If you're thinking about DateUtils.getRelativeTimeSpanString(): don't.

After a long struggle I've finally given up on the method.  It seems so tempting - an easy way of showing the user how far away an event will be (or was).  But without being able to pass a timezone you run into an intractable situation which makes it unusable.

The critical problem relates to how it calculates a number of days.  It relies on Time.getJulianDay(), which would work - except you have no control over the timezone parameter.  Instead, it uses the default device timezone.  This can cause day calculations to be incorrect as you might shift a Julian day when applying the device timezone to the milliseconds provided from another timezone.

Before 4.3, the situation was even worse: the internal Time used in DateUtils (for calls to getJulianDay()) is cached.  That meant if your app uses getRelativeTimeSpanString(), then the device's timezone changes, you're now calculating the # of days based on the previous device timezone!

The only comprehensive solution I can come up with is to implement my own version of getRelativeTimeSpanString() that adds the timezone element.  You just need to account for timezones when you're dealing with any period of time greater than hours.

Thursday, October 3, 2013

Centering Single-Line Text in a Canvas

Suppose I want a custom View that draws a circle, then a number centered inside of it.  Your first attempt will probably look something like this:


The problem is that while you can easily set a horizontal alignment for your TextPaint (via Paint.Align), the vertical alignment is tricky.  That's because Canvas.drawText() starts drawing at the baseline of your set Y-coordinate, instead of the center.

If you only knew the height of the text, then you could center it yourself - but getting the height is tricky!  TextPaint.getTextBounds() doesn't work quite right because it gives you the minimal bounding rectangle, not the height that the TextPaint draws.  For example, if your text has no ascenders/descenders, then the measured height is smaller than it will draw (since it will still account for the possibility of them).

The way I've found to get the height of the TextPaint is to use ascent() and descent().  These measure the size above/below the text's baseline.  Combined, they add up to the total height of the drawn text.  You can then use some math to center the draw on the baseline - here's a version of onDraw() that does it correctly*:

protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);

  Paint paint = new Paint();
  paint.setColor(Color.BLACK);

  TextPaint textPaint = new TextPaint();
  textPaint.setColor(Color.WHITE);
  textPaint.setTextAlign(Paint.Align.CENTER);
  float textHeight = textPaint.descent() - textPaint.ascent();
  float textOffset = (textHeight / 2) - textPaint.descent();

  RectF bounds = new RectF(0, 0, getWidth(), getHeight());
  canvas.drawOval(bounds, paint);
  canvas.drawText("42", bounds.centerX(), bounds.centerY() + textOffset, textPaint);
}

And the finished product is here:


Note that all advice in this post is about a single line of text.  If you're handling multi-line text then the solution is more complex because you have to handle how many lines the text will render onto.  If I ever try to tackle that I'll write that up as well.

* In a real-world example, you wouldn't want to instantiate your Paints in onDraw(); this is done for brevity's sake.

Tuesday, September 17, 2013

Upcoming Conferences

I just wanted to let anyone interested know about some upcoming conferences I'll be speaking at...

Droidcon London
London, October 24-27th
Crafting Unique, Delightful Apps (9:45 AM, Thursday, October 24th)

I'll be giving this talk in conjunction with our awesome designer, Chris Arvin.  To be honest, 45 minutes is hardly enough time to talk about all the things we could, but I'll be around all week long to talk more.

I'm also planning on attending the hackathon that weekend.  I've never done a hackathon before so it should be fun.  My only concern is that I have zero interest in staying up all night coding.  I think 8 hours of sleep (and some mental rest) is far more valuable than being drained all Sunday.

AnDevCon
San Francisco, November 12-15th
I Can Animate and So Can You (8:30 AM, Friday, November 15th)

I've started embracing animations in the last year in a way I never had before.  I regarded them as chunky and painful, which was true back in the days of 1.x and 2.x, but that's no longer the case.  It's possible to make really awesome animations now but it's still difficult to fit them into your app's structure.  This talk is designed to get you to think about how to do advanced animations in your app.

I also have a discount code for AnDevCon.  It'll knock $200 off registration.  Just use the code "LEW".

Tuesday, September 3, 2013

Smoothing performance on Fragment transitions

Suppose you're doing a pretty standard Fragment replacement with a custom animation:

getSupportFragmentManager()
    .beginTransaction()
    .setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out)
    .replace(android.R.id.content, new MyFragment())
    .commit();

You may notice that the performance can be a bit rough, not as smooth as you'd like. A common way to improve Android animation performance is to use hardware layers.  Normally you'd add it to the animation directly but with fragments you don't get access to it unless you take advantage of Fragment.onCreateAnimation()*.  Here's how it looks:

public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
    Animation animation = super.onCreateAnimation(transit, enter, nextAnim);

    // HW layer support only exists on API 11+
    if (Build.VERSION.SDK_INT >= 11) {
        if (animation == null && nextAnim != 0) {
            animation = AnimationUtils.loadAnimation(getActivity(), nextAnim);
        }

        if (animation != null) {
            getView().setLayerType(View.LAYER_TYPE_HARDWARE, null);

            animation.setAnimationListener(new AnimationListener() {
                public void onAnimationEnd(Animation animation) {
                    getView().setLayerType(View.LAYER_TYPE_NONE, null);
                }

                // ...other AnimationListener methods go here...
            });
        }
    }

    return animation;
}

Now the animation should be a lot more smooth!  In my own code, I've overridden this method in a base Fragment from which all others extend so that I always get this feature (though if you're more particular you could only apply it to certain Fragments).

* If you're not using the support library, then you'll be overriding Fragment.onCreateAnimator() and using animator-based classes.

Tuesday, August 20, 2013

Joda Time's Memory Issue in Android

I've recently gotten fed up with how nuts the built-in calendar library is for Java/Android.  It's incredibly easy to make mistakes and it's unintuitive at best, so I've finally decided to take the plunge and switch to Joda time.

Joda is like a dream come true* except for one fairly extreme memory issue that I ran into.  After adding it to the app we started to see two huge memory sinks show up in MAT: a JarFile and a ZipFile that in our app took up a combined 4MB!



Joda's JAR is only half a meg, so how come these things took up so much space?  Why didn't any other JARs take up this space?  What's even stranger is that the amount of memory used seemed to scale based on the number of resources I had in the application; a simple test app only used up an extra 700kb, but Expedia's resource-heavy app took up the above.

It turns out the problem is ClassLoader.getResourceAsStream().  Joda time includes the olson timezone database in the JAR itself and loads the TZ data dynamically through getResourceAsStream().  For some reason getResourceAsStream() does some rather extreme caching and takes up a ton of memory if you use it**.

Thankfully there's a fairly simple solution.  You can actually implement any timezone Provider you want, circumventing the normal JAR-based ZoneInfoProvider.  Just make sure that your implementation has a default constructor and setup your system properties thus:

System.setProperty("org.joda.time.DateTimeZone.Provider",
    AssetZoneInfoProvider.class.getCanonicalName());

As such, I imported all of the TZ data (compiled, from the JAR) into my project's /assets/ directory.  Then I took the source for ZoneInfoProvider and reworked it so that openResource() uses the AssetManager to retrieve data.  I hooked it up and voila - no more excessive memory usage!  As an added bonus, this makes it a lot easier to update your TZ data without relying on a new version of Joda time.

As an epilogue, if someone can explain why getResourceAsStream() causes the sadness it does I'd be interested to know.  I tried looking into it for a bit but gave up because it wasn't like I would be able to change the system code anyways.

* Seriously: if you deal with dates, times, or some combination thereof at all, you will be doing yourself a favor by switching to Joda time.

** What initially tipped me off was a Jackson XML post about the same problem: https://github.com/FasterXML/jackson-core/pull/49

Wednesday, July 3, 2013

Don't Override ListView.getAdapter()

A few days we ran into a bug.  When we were updating the underlying data for the particular Adapter, the ListView would blow up with this exception:

E/AndroidRuntime(1809): java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. [in ListView(16908298, class com.expedia.bookings.widget.ItinListView) with Adapter(class android.widget.HeaderViewListAdapter)]

The thing is, we weren't updating from a background thread.  That exception is thrown when the # of items the ListView thinks the adapter has and the # it actually has are out of sync, but I couldn't see how that was happening.  What was going on?

It turned out to be me overriding ListView.getAdapter().  There's some code I've been working with recently which has a custom ListView; inside of it is a custom Adapter.  I wanted access to that Adapter (but not the ListAdapter wrappers that are sometimes added in the case of header/footer views), so I overrided ListView.getAdapter() and had it return the custom Adapter.

However, the ListView uses getAdapter() sometimes to get the count for the # of items it has.  By bypassing the wrapper ListAdapter, the count was sometimes wrong (since it wasn't accounting for header/footer Views).

The moral of the story is: don't override ListView.getAdapter().

Thursday, June 27, 2013

How to Correctly Format Date/Time Strings on Android

One aspect of internationalization is to correctly format your date/time strings.  Different countries use very different formats and it's easy to incorrectly format your strings for your international users.

Your first foray into formatting date/times is probably through java.text.DateFormat (via SimpleDateFormat):

Calendar cal = new GregorianCalendar(2013, 11, 20);
DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
String date = df.format(cal.getTime());
// date == "2013-12-20"

While this works great for formatting parameters (for, say, a web service), it's terrible for localization.  Your international users won't be using the same date/time format you're using and it won't pick up user preferences (e.g., date order or 12-hour vs 24-hour).

A more correct way of doing it is to use android.text.format.DateFormat (not to be confused with the previous DateFormat).  There are some methods here that return formatters defined by the system's locale, like getDateFormat() and getTimeFormat() (among others):

Calendar cal = new GregorianCalendar(2013, 11, 20);
DateFormat df = android.text.format.DateFormat.getDateFormat(this); 
String date = df.format(cal.getTime());
// date == "12/20/2013"

The problem with these formatters is that they are inflexible; what if you don't want to show a year on a date?  What if you want to include the day of the week?  There are only limited circumstances where these formatters are good enough.

The best solution is to use DateUtils.  It has two powerful methods - formatDateTime() and formatDateRange() - which take in flags to determine which fields to include.  It automatically formats to the user's locale and preferences without you having to worry about it.

DateUtils.formatDateTime() formats a single point in time.  Here's a few examples:

Calendar cal = new GregorianCalendar(2013, 11, 20);
String date = DateUtils.formatDateTime(this, cal.getTimeInMillis(), DateUtils.FORMAT_SHOW_DATE);
// date == "December 20"
date = DateUtils.formatDateTime(this, cal.getTimeInMillis(), DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NUMERIC_DATE | DateUtils.FORMAT_SHOW_YEAR);
// date == "12/20/2013"
date = DateUtils.formatDateTime(this, cal.getTimeInMillis(), DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NUMERIC_DATE | DateUtils.FORMAT_SHOW_TIME);
// date == "00:00, 12/20/2013"

DateUtils.formatDateRange() formats a range, like "Jan 5 - Feb 12".  Why might you want to use this instead of just concatenating two calls to formatDateTime()?  Besides being easier, it can optimize output in certain circumstances by reducing redundant field usage, like months/years when they don't change throughout the range:

Calendar cal1 = new GregorianCalendar(2013, 11, 20);
Calendar cal2 = new GregorianCalendar(2013, 11, 25);
Calendar cal3 = new GregorianCalendar(2014, 0, 5);
String date = DateUtils.formatDateRange(this, cal1.getTimeInMillis(), cal2.getTimeInMillis(), DateUtils.FORMAT_SHOW_DATE);
// date == "December 20 - 24"
date = DateUtils.formatDateRange(this, cal1.getTimeInMillis(), cal3.getTimeInMillis(), DateUtils.FORMAT_SHOW_DATE);
// date == "December 20, 2013 - January 4, 2014"

One thing to watch out for with formatDateRange() is where it cuts off the day.  You may notice in the example above that the date ranges seem to be off by one day; that's because it cuts off at midnight.  If you add a millisecond it should properly format the range.

If you want your application to abide by the locale's formatting rules while still having control over what information to show, DateUtils is your place to go.  Be sure to read through all the different formatting flags so you can wield the most power with this tool.

(One final note - the example code above shows output in my locale.  In your locale it may differ - this is on purpose, of course!)

Tuesday, May 14, 2013

Sentinels of the Multiverse Randomizer App

My latest side project is a randomizer application for the board game Sentinels of the Multiverse.

You can grab the app here: https://play.google.com/store/apps/details?id=com.idunnolol.sotm

You can check out the source here: https://github.com/dlew/android-sotm

I have to admit that writing it was a gigantic waste of time (the benefit is minuscule in comparison to the time it took to write).  However, it was a  ton of fun to make; there's something very liberating about writing an app from scratch with no baggage to worry about.  It was also liberating to write an app that uses modern APIs with complete disregard to backwards compatibility.  As such, it's only available on ICS+.

In other news, I will be at Google I/O again this week.  In particular I will be helping man Expedia's booth in the Android sandbox; feel free to come by and say hello.

Tuesday, April 30, 2013

Before & After

I attend weekly trivia nights with friends.  One of the categories, Before & After, has piqued my interest.  It works like this: two movies are described, and you have to name both movies.  What makes it a bit easier is that the two movies share a common combining word.  Here are a few examples:

  • Men in Black Swan
  • Sling Blade Runner
  • Law Abiding Citizen Kane
  • My Fair Lady and the Tramp
  • Batman & Robin Hood
  • A Walk to Remember the Titans

I find the answers to be fairly amusing, so I wrote a program that generates before & afters.

You can find the source code here.

I've pumped out a spreadsheet of results (for movies) here.

I used a word list to discover words in common between the start/end of movie names.  For finding the common word, I generated both a forwards and a backwards trie of the first/last word of each movie name.  Tries are pretty much the best for simple text searches.

Ultimately, the program is general enough that it could work on anything.  The main reason I focused on movies was because IMDB is good enough to dump their ratings data, which helped immensely for sussing out good vs. bad results.  I found the best way to rank results was a combination of the # of voters (showing popularity of both items) and the difference in their rating (in that combining two items with vastly different ratings is hilarious).

There was one problem I could not overcome (since I was putting relatively minimal effort into this venture), which is determining when a word was part of a compound word or not.  For example, I would want to match "ball" in "basketball" because "basketball" is a compound word, but I wouldn't want to match "all" in "hall".  Solving this problem would a word list with more data than the word itself, so I just skipped it.

Tuesday, April 9, 2013

Memory Management: A Case Study

During the development of the most recent version of Expedia's Android app we ran into a variety of memory problems.  I thought I'd share some details about the problems we ran into and how we solved them.

Memory Analyzer Tool (MAT)

If you're not familiar with Eclipse's MAT you should be.  MAT should be your first stop for dealing with memory problems.  Not only is it useful for finding out what's hogging memory, it can also be used to find memory leaks.

An important note about using MAT - almost all memory problems I've run into relate to Bitmaps.  In versions of Android previous to 3.x, Bitmaps were stored in native memory (rather than the heap) and thus not viewable in MAT.  This makes it much harder to debug memory problems on 2.x devices, so you might want to stick to 4.x devices for investigating memory issues.

High-Resolution Bitmaps

Bitmaps in memory are uncompressed (even if they were compressed in your APK).  This can cause serious issues if you have high-resolution assets.  For example, we had tablet backgrounds which were the full resolution of the N10 (2560x1600).  That's fine with the N10's beefy heap size, but when scaled down to smaller devices (like the N7) the scaled image would take up 1/8th of the app's memory!  Imagine doing that multiple times and you can see why we ran into memory issues.

We solved this problem one of two ways: first, we reduced the resolution of the asset.  Some of these backgrounds were blurred, meaning we could lower the resolution greatly without losing quality.  When we couldn't lower the resolution without quality suffering greatly, we would sometimes replace the normal asset with a backup color (when we detected low memory situations).

BitmapFactory.decodeResource()

When you're decoding resources yourself (rather than using something like ImageView.setImageResource()), there are a couple things to look out for.

If you're decoding the same resource over and over again, you might want to cache it.  This problem hit us because we had an ImageView in a list show a default asset before we load an image from the network.  If you scrolled through a list quickly, and each one is loading its own default asset, you could create a burst of memory usage that runs you out of memory.  By caching this default, we not only saved memory but also improved performance.

Another issue we ran into with decodeResource() involves device density and BitmapFactory.Options.  If you are using them you should know that the default is to not auto-scale the asset at all.  So if you've only got an xhdpi version of the asset on an mdpi device, you're going to load them in at xhdpi size, taking up more memory than you expected.  So make sure to set that if you're manually loading assets with Options.  (If you're not passing BitmapFactory.Options you don't need to worry, as the default decodeResources() handles density for you.)

LruCache

Having an in-memory LruCache of Bitmaps loaded from the network is all the rage these days. However, it can easily lull you into a false sense of security.  Just because you've limited your Bitmap cache doesn't mean you'll never run out of memory!  If your LruCache is 1/4th the size of available memory, but your app (at one point) needs all of it not to crash... well, you've got a problem.

Things got better for us once we started manually clearing the LruCache at key moments.  Having a DiskLruCache helps a lot if you do this, as you can clear the memory cache but then quickly reload images if you need to without going to the network.

Resources and sPreloadedDrawables

This is a red herring!  You may see in MAT that Resources is taking up a whole ton of memory with a variable called sPreloadedDrawables.  Unfortunately, you can't avoid sPreloadedDrawables; it's a pre-defined set of system resources that are preloaded into memory for every app.  Don't spend any time thinking about them.

Recommendation

Overall, what you should do is find an old phone and use it as your primary development device.  That way you know from the start that your app runs on all phones.  It's much harder to discover these limitations later on.

For example, the Nexus 4 (my primary device) has a whopping 192MB of memory.  Even with all xhdpi assets that thing is difficult to run out of memory on a standard app.  Compare that to what QA was using - devices with 24MB to 32MB.  It's a very different game when you're trying to design a beautiful app to run in 24MB on hdpi!

Tuesday, April 2, 2013

Automatically Managing Bitmaps from URLs via UrlBitmapDrawable

If you develop apps, you'll probably (at some point) need to load images from the internet.  Sometimes you'll load many images at once, as in a ListView or Gallery.  Your first foray will be easy, using BitmapFactory to decode a stream - then you run into the dreaded OutOfMemory error and are suddenly grounded back to the reality that is limited memory space.

There are two equally important problems to handle when using images: loading an image to view it, and unloading it later to save memory.  Last time I looked there were plenty of libraries out there for solving the first problem, but I was unsatisfied with their solutions to the second.  A lot of them simply blow away loaded Bitmaps at some point in time - but what if you still wanted to use it?  Then you end up in this complex tightrope walk, where you're having to constantly check and re-check your Bitmaps for whether they still exist.  It's a gigantic pain and I've been doing this dance forever.

I set out to solve the problem in a better way, such that it would do three things:

1. Load images from a URL into a View.

2. Unload that image later (for memory's sake).

3. Re-load that URL into the View if we ever see it again.

I was able to accomplish all three with the help of a class I wrote, UrlBitmapDrawable.

Introducing UrlBitmapDrawable


You can check out the code here: https://gist.github.com/dlew/69e6557604926d7e1513

You can use it just about anywhere as a Drawable.  Just instantiate it then use it:

UrlBitmapDrawable drawable = new UrlBitmapDrawable(res, myUrl, myFallback);
myView.setBackgroundDrawable(drawable);

With ImageViews there's a bit of a hack I needed to do (in order not to have to use a custom ImageView).  So I setup another method for using UrlBitmapDrawable with ImageViews:

UrlBitmapDrawable.loadImageView(myImageUrl, myImageView, myFallback);

Highlights


This solution has made my life easier in four ways:

It's Simple - All you need to is provide it a URL and it takes care of the rest.  You don't ever have to worry about its state; if the underlying Bitmap is recycled, it will fallback to the default image and start reloading the URL.

It's a Drawable - By making it a Drawable, it meant I could attach it to any View.  Tying it to a custom View would have vastly limited its potential.

It's Compatible - It's not tied to any particular implementation for loading images.  Retrieving images could be a simple network call, or you could hook it up to a complex LRU memory/disk cache.

It's Extensible - The version I've provided is simple; internally we've added some bells and whistles to it.  See "Areas for Improvement" below.

Disadvantages


It's not all sunshine and daisies.  There are two problems with UrlBitmapDrawable; however, I considered them minor in comparison to the larger problem I was trying to solve.

BitmapDrawable - BitmapDrawable does not let you access the underlying Bitmap by default, so you'll have to import your own version that opens it up.  Here's the source code from github.

ImageView Hack - In order to get ImageView to re-measure the Drawable's width/height after loading you have to trick it into thinking the Drawable has changed (by nulling it then resetting it back).  To be honest, there might be a better solution here, but I haven't found it.

Areas for Improvement


Here's ways that we've tricked out our UrlBitmapDrawable:

Default Resources via Id - A default resource is important (so we can show the user something before we load the asset).  In the sample code the UrlBitmapDrawable holds a Bitmap; this is a fine example, but if you're inflating a new default Bitmap per UrlBitmapDrawable, that can wreak its own memory havoc.

Internally, we've gone a more complex route which uses Resources and resIds, and loads the Bitmap automatically from an in-memory cache.  It keeps us from spending a lot of time (and memory) reloading the same default bitmaps.

Fallback URLs - We are sometimes given a list of URLs at differing quality levels.  It's pretty easy to hook that into this system; each time a download fails, try the next URL.

Recycling Bitmaps - If you read the code carefully you may notice that I never actually dealt with the second step - unloading Bitmaps from memory.  We use an LruCache to handle this; as a result of UrlBitmapDrawable it can recycle Bitmaps with impunity.  It also means you can evict the entire cache at any time if you need the memory.

Wednesday, March 20, 2013

Is Your AsyncTask Running?

A short note about AsyncTask: Prior to 4.0 (and maybe prior to 3.0, but I haven't tested), AsyncTask.getStatus() might lie to you. If the AsyncTask is canceled, it won't set the status correctly; instead, it will remain RUNNING far after AsyncTask.onCancelled() finishes.

My initial thought was to use AsyncTask.isCancelled(), but you can run into some concurrency issues there if you're trying to gauge whether the AsyncTask is done from another thread.  A cancelled AsyncTask doesn't necessarily end the moment you cancel it; in fact, if you're not checking isCancelled() regularly in doInBackground() then you can end up having the AsyncTask run for a while after you cancel.

My solution is to set a boolean at the end of onCancelled() that will indicate to the system that you got to the end of execution.  Here's an example of writing an AsyncTask where you can properly know when it's been finished:

private class MyAsyncTask extends AsyncTask {
  private boolean mFinishedCancel = false;

  protected Void doInBackground(Void... params) {
    return null; // You'd normally do something here
  }

  protected void onCancelled() {
    mFinishedCancel = true;
  }

  public boolean isFinished() {
    return getStatus() == Status.FINISHED || mFinishedCancel;
  }
}

Tuesday, March 12, 2013

Easier View State Saving

If you write your own custom Views you may eventually want to leverage onSaveInstanceState() and onRestoreInstanceState() to store some data relating to the View.  Unlike Activity/Fragment, which use Bundle to pass around the instance state, Views use Parcelables for their instance state.

Parcelable is fast, efficient, and kind of a pain in the ass to setup.  It's doubly painful for custom Views as you have to ensure that you're properly saving the superclass' state as well in your own Parcelable.  The boilerplate code is excessive.

It turns out there's a much easier way which leverages these two points:

  1. Bundles implement Parcelable.  This means you can use a Bundle instead of a custom Parcelable as your instance state.
  2. Bundles can store Parcelables.  This means you can preserve the superclass' state in your Bundle.

Here's a simple implementation of the concept:
public class CustomView extends View {
  public CustomView(Context context) {
    super(context);
  }

  @Override
  protected Parcelable onSaveInstanceState() {
    Bundle bundle = new Bundle();
    bundle.putParcelable("superState", super.onSaveInstanceState());
    // Put whatever you want into the Bundle here
    return bundle;
  }

  @Override
  protected void onRestoreInstanceState(Parcelable state) {
    Bundle bundle = (Bundle) state;
    super.onRestoreInstanceState(bundle.getParcelable("superState"));
    // Restore what you put into the bundle here
  }
}

At this point you can store any primitives in a Bundle as you normally would in an Activity/Fragment.  Plus, if you use this trick many times you could even wrap it in a subclass of View (and simply replace the default instance state with a Bundle version of it).

Tuesday, February 26, 2013

Resistance/Avalon App

I've once again taken a detour from my more serious Android development to do some silly side projects.

The first of them is an app for the board game The Resistance and The Resistance: Avalon.  I've recently become obsessed with this game because it's just a ton of fun.  The game involves a lot of hidden roles, so there's a lengthy boot-up sequence where each person's allegiances are determined.  When you use all the roles available, it gets to be a bit of chore.  So I've written a dumb app that uses Android's TTS to speak the setup out loud, making the process a tiny bit easier.

The application can be found here: https://play.google.com/store/apps/details?id=com.idunnolol.resistance

Open source code here: https://github.com/dlew/android-resistance

As is usually the case with side projects, the time I put into the project vastly outweighs the time I'll ever save by using the app.  As such, I took this as an opportunity to try out two things: Android's TextToSpeech capabilities, and Maven Android builds.

TextToSpeech

I found TextToSpeech to be far easier to use than I expected.  It took me almost no time to get it up and running.  The only snag I ran into was using the OnUtteranceCompletedListener.  You need to give an utterance id to something you play before the listener will fire:

HashMap<String, String> params = new HashMap<String, String>();
endParams.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "myUtteranceId");
mTTS.speak("Say Something", TextToSpeech.QUEUE_ADD, params);

Maven Android Builds

I've been woefully behind the times with regards to Android build technology.  For years I've seen open source github projects using Maven but I've always ignored it because I'm scared of the big angry pom.xml files.  So I determined that I would use this simple app to teach myself Maven (via maven-android-plugin).

I found the initial setup of Maven to be pretty simple.  I had the samples up and running in no time using the "getting started" section of the site.  I even got Eclipse building the application using Maven using m2e-android.  So far so good.

I ran into a brick wall when I tried to add a library (in particular, ActionBarSherlock).  The command line Maven worked just fine when I added the library dependency, but I happen to enjoy the amenities of a modern IDE so it must work in Eclipse.  But in Eclipse, it wouldn't build - it complained about a missing dependency.  It turns out that you need to still manually do stuff for each library anyways if you're using Eclipse + Maven (unless I'm mis-reading the state of apklib, which is entirely possible).  Wasn't that the whole reason I started to use Maven in the first place?  To simplify my build process?

I think I'll keep making pom.xml for command line building/testing, but for actual dev in Eclipse it actually sets me back to use Maven.  Perhaps it integrates better with IntelliJ?  That alone may be reason to switch.  But at this point I'm far more excited for the upcoming Gradle builds.

One More Thing

If there was one cool thing I did with the code, it was the setup of Config.java.  Originally I had it with a bunch of booleans, one for each option; but this led to a lot of switch-like code that just felt repetitive. By converting it to an enum keyed-boolean store, I was able to automate a lot of app.  I always love it when you can greatly simplify and condense the code at the same time.