I spoke at Droidcon NYC 2014 on the topic of ‘Writing Maintainable Android Applications’. I had a few requests for the slides so wanted to get them posted immediately. I also have an example application on GitHub that further illustrates the patterns I mention in the talk.
In the past I have used Jenkins as my primary continuous integration tool. It works for Android development, but I would never describe using it as a great experience. Jenkins has seemingly unlimited flexibility, at the cost of taking significant time to wire up the common tasks. It also has one of the worst UIs I have ever used.
I recently tried out TravisCI and found it much more pleasant. Travis doesn’t seem to be as configurable as Jenkins, but the main uses cases are much simpler to setup. The UI is also much cleaner. Hosted TravisCI is free for open source projects, and there are a few price tiers for commercial projects.
One of the main benefits of switching to Gradle for your Android build system is the ability to create multiple packages from a single commit. This enables installing debug & release versions of the same app on a single phone, as well as more complicated flavor setups such as free/paid versions. Although setting up the actual package naming is simple, unfortunately some Android systems breakdown when the package name varies. One common problem is INSTALL_FAILED_CONFLICTING_PROVIDER errors when your app includes Content Providers.
Android applications invariably seem to have many places where you have to pack data into an Intent or Bundle in order to pass it on to another component. I have found that packing a single Java object in the payload is far more maintainable than using several primitive key/value pairs. If you decide to add or remove an a field to the object later, you won’t have to go back and modify every single Intent & Bundle. In order to do this, the object must implement either Serializable or Parcelable. I generally prefer Parcelable, even though it takes a bit more code to implement.
Unfortunately I ran into a huge pitfall with Parcelable objects. I have been using a very basic unit test to ensure my Parcelable implementation was correct, and didn’t realize the test wasn’t actually proving everything was working as intended. The other day I was refactoring some code and seeing ClassCastExceptions when pulling a particular Parcelable object out of a Bundle. I couldn’t figure out where the type was getting switched in my code, but it never occurred to me that the actual Parcelable implementation was at fault because I had too much faith in my unit tests. After a couple hours of digging I figured out that the naive unit tests I have been using for Parcelable don’t actually ensure anything. Hopefully this post can help you avoid the same mistake.
How often do you see the “Re-installation failed due to different application signatures” error when trying to install a development APK? By default the Android build chain uses a local debug signing keystore, so the same commit built on two different machines will be signed differently and can not re-install on top of each other. This is easily fixed by putting a shared debug keystore into your project repo. It’s a simple time saver if you sometimes use different computers, have a continuous integration server, or work with a team.
The official Android developer documentation has a nice overview of why you should use Proguard when building your application. However, that documentation does not yet cover the new Gradle build system.
Android’s Gradle build system has had a built-in task for running ProGuard for a while now. The Android Tools site has a small section about ProGuard, but it misses a few crucial details.