Best Practices for Android Developer Productivity

by Sergii Zhuk - 28 Jul 2016

The efficiency of your software engineering work depends not only on your deep knowledge and expertise, but also on the toolset, proper environment configuration, and team collaboration activities.

I recently gave a talk at Droidcon Berlin about the best practices for Android developer productivity that we use in our Zalando Tech team. Below you can find the key points from my talk which will make your developer life more pleasant and your app more stable.

What does your AndroidManifest really look like?

A lot of us already know that the AndroidManifest.xml you see in the text editor is not always the same as the one that will be included in the application build. It happens mainly because the libraries you are including in your project may contain extra <uses-permission/> elements in their manifests, which will be blended with permissions requested in your manifest file (see The Commons Blog for more details).

To check your manifest before the APK build, we can use a new feature presented in Android Studio 2.2: Merged Manifest Viewer. This tool will show how your AndroidManifest merges with your project dependencies based on build types, flavors, and variants. You can reach this tool by navigating to your AndroidManifest.xml and clicking on the new Merged Manifest bottom tab.


Support annotations are your friends

Another extremely useful tool is a support annotations library. You can include it in your project by adding “” to your build.gradle file. Use these metadata annotations to decorate your code to help catch bugs and define code rules. The most common use-cases for them are marking nullable and non-nullable, identifying integer as resource, and specifying from which thread they should be called.

Since these annotations are metadata annotations, your project will compile even if you violate rules defined by them. However, it will be highlighted by Android Studio and Lint, and will be visible to other team members in your Continuous Integration tool output.

Fast and Painless code review

Assume that you’d like to do a code review. It makes sense to check how the developed feature works, so you’ll need to compile the project. The common workflow for this case is the following:

  • Stash changes on your current branch
  • Checkout branch to review
  • Reload gradle config in your IDE
  • Read the code in IDE
  • Compile and launch, then test the app
  • Return to your work by repeating actions (1) - (5) for your branch

“What’s the problem with it?” – you could say. Yes, everything is fine. But not for the case when you have a project with 1000+ classes and different build configurations – you can easily spend more than three minutes on a powerful Macbook waiting for the new code to be compiled.

Our solution is to use a dedicated IDE instance and repository folder for the code review. In this case, your work won’t be stopped for a while and you can come back to your main IDE and branch at any moment. Just a small disclaimer: We recommend you use a machine with at least 16GB of RAM, as the time you’ll save is definitely worth it.

Apply changes fast

Even if you have a small Android project, you always should spend some time waiting for the build and deploy of your latest changes to the test device or emulator. If you have hundreds of classes and xml layouts, each rebuild and re-deploy can cost you a lot of time – even on powerful machine. Moreover, you will need to navigate manually to the application screen where you changed something, which also requires some effort.

At the end of 2015, the Android Community received two tools to allow code changes to be applied faster. The first of these was JRebel, which comes from the Java backend world where it has been the industry standard for a long time. Another tool was announced by the Google team together with Android Studio 2.0 – Instant Run. Both of these tools have the same aim, but JRebel contains more features and has an annual license you have to pay for.

I haven’t found any independent comparison of these tools, so I’ve made the table below analyzing their documentation and available blog posts. As for now, the most common use-cases for both of them are changing resources and method logic without structural changes such as interfaces or manifests.


Reto Meier: "Instant Run: How Does it Work?!"
Oleg Selajev: "Looking at JRebel for Android and Instant Run ..."

Both tools are still in the active development phase and improvements are coming almost every week. From our experience, a lot of use-cases are still not covered but you can already benefit from using these tools if you know what to expect from them.

Measure execution time

Another extremely useful feature during application debug and performance analysis is logging method input/output and execution time. For these needs, we use a simple and elegant method annotation tool - Hugo by Jake Wharton. It works like a charm if you just want to read the log and don’t want to use deep and complicated tools like Systrace.

All you need is to annotate the target method as shown:

public String getName(String first, String last) {/* ... */}

And find the respective information about the method call printed in logs:

V/Example: --> getName(first="Jake", last="Wharton")
V/Example: <-- getName [16ms] = "Jake Wharton"

How to read logcat output from your device

For everyday needs, most of us read logs using Android Monitor inside Android Studio. It works fine for the simple cases, but we note several trade-offs with this approach:

  • Logs are hard to read, you should use external tools or configs to do formatting
  • Android Studio logging tools are attached to the process ID of the application you’ve deployed. If you redeploy the app or kill the process, your previous logs will be lost because Android Studio attaches log tool by process ID

As a solution to this problem, we can use another tool by Jake Wharton – pidcat. The main benefits of it are:

  • Good color schema and formatting
  • Connect to the debugged application by package name, not process ID. All logs will be kept after re-deployment of the app

Network output logging and analyzing

The most common and obvious way to read your app network interaction logs is to use the log output from your HTTP client library. However, this approach has several trade-offs:

  • If you keep full network logging enabled during the development, you will note that performance of the app has decreased – it takes some time to print the log
  • If your app has some external libraries using networks for their needs (for example, Google Analytics)  you may need to do extra configuration changes for each of these libraries to force all data to be logged
  • During QA it can be impossible to have access to the console output and specific configs, thus you won’t be able to monitor traffic this way on the production app build

There is another approach: Use Http Monitoring and Proxy tools like Charles Proxy. These type of tools can provide the following functionality, wrapping your app as a black box:

  • HTTP/HTTPS traffic monitoring and recording
  • Rewrite values and try edge cases of server response
  • Set breakpoints on the network calls
  • Install SSL certificate to the device to read the encrypted traffic

Keep testing on various OS versions

One thing I’m always doing and keep pushing my colleagues to do is to test each feature during the developer test on both Lollipop or higher (API 21+), and pre-Lollipop devices or emulators. This might sound like a Captain Obvious tip – but I catching these bugs regularly during testing.

The most common bugs you can discover this way are touch feedback and system colors issues. We often saw app crashes on older APIs due to some compatibility issues.

Automate screen interaction

Often we need to check some scenarios and do repetitive UI clicks/inputs on various devices. It can be quite annoying if you have three to four test devices and you need to go through a regression plan with 30 scenarios.

The first step of automation we can do is typing adb commands or even whole scripts to not interact with the device by hand every time. For example, adb shell input keyevent 4 will submit a UP button press to the connected test device. This way you can pass system key press, keyboard input, and screen touch.

But what do you do if you have three devices for the same test scenario? We use adb-ninja script by Roman Nurik to submit commands to several devices simultaneously. It’s a tiny shell script which sends the typed adb command to all connected devices and saves a lot of time.

Check your build.gradle configuration

Even experienced developers sometimes follow outdated configuration practices. Let’s check your build.gradle file and see if you’re one of them:

  • Get rid of mavenCentral and use jcenter as the dependencies repository. jcenter has faster response time and now acts as a superset of mavenCental content.
  • Check Android Plugin for Gradle version, since having the latest version can increase build performance and contains sweet tools such as Instant Run.
  • Don’t specify version ranges for the dependencies. Always use a constant version value like “23.4.0” to be secured from unexpected dependency API changes, so as not to do a network call for the latest version check for each dependency on every build.
  • Use build flavors to setup the build for minSdkVersion 21 or higher during the development. It will help to build faster using all improvements that the Android Tools team provides for us.

And that’s it! In this post we discussed 10 tips to increase the efficiency of your everyday developer work and build high quality apps following my talk at Droidcon Berlin 2016. The full version of slides were published at Speakerdeck.

Feel free to contact me on on Twitter @sergiizhuk with your questions and comments.

Similar blog posts