Glaring Importance

Monday, November 7, 2016

My team at LinkedIn just released a new version of Test Butler, an Android testing utility that makes your tests more stable and reliable.

The headline feature is the ability to grant runtime permissions from Test Butler on Android Marshmallow and above. Since Test Butler is a system-level application, it can grant these permissions for you without any interaction from the user; you just need to call a single method and the permission will be enabled for your app.

TestButler.grantPermission(permission)

Many Android developers are already granting runtime permissions in their tests by passing the “-g” flag when installing their app for testing:

adb install -g MyApp.apk

Unfortunately, this flag can’t be set when running tests from Android Studio. There is an open feature request in the Android bug tracker asking for support for this, but in the meantime, Test Butler now provides a workaround that developers can use when running tests from Android Studio.

Test Butler currently doesn’t provide a way to revoke runtime permissions from tests, because this feature wouldn’t be very useful; revoking a runtime permission will kill your application process, causing your test to fail.

In addition to permission granting, several bugs and user feature requests have been addressed; check the changelog for the full list. For more info on Test Butler, check out the original announcement blog post.

Saturday, August 6, 2016

If you're an Android developer, check out Test Butler, a new open source Android testing utility from my team at LinkedIn. Simply installing this on your Android emulator and adding one line of code will automatically make your tests more stable and reliable. For more details, head over to the announcement post on the LinkedIn Engineering blog or visit the GitHub repository.

Saturday, April 23, 2016

I've been running Android UI tests at my company using the API 22 revision 1 emulator (Android 5.1). Things have overall been pretty stable, but since most devices have received an update to at least Android 5.1.1 (the revision 4 emulator of API 22), I wanted to update the emulator we were using to run our tests as well.

When running Espresso UI tests, we found that disabling the software keyboard during tests made them significantly more stable. We did this by setting the following line in our AVD's config.ini file:

hw.keyboard=yes

Configuring our emulator have a hardware keyboard was enough to tell Android not to show the software keyboard while typing in input fields. However, in Android 5.1.1, this behavior changed. A new setting was added in Android 5.1.1 to the "Language & Input" section of the Android OS Settings app:

The new part is the toggle switch, "Hardware - Show input method". By default, it's enabled, which means that even if the device has a hardware keyboard (or in our case, our emulator is pretending to have a hardware keyboard), the software IME will still be displayed on screen.

This had a significant impact on our tests, and was causing many tests to fail. For example, a test that called the Espresso method pressBack() and expected to close the current activity would fail because the pressBack() call closed the soft keyboard instead of closing the activity.

After quite a bit of searching, I found that it's possible to toggle this OS setting using an ADB command:

adb shell settings put secure show_ime_with_hard_keyboard 0

Once we found this command, we modified our emulator startup script to call it after launching the emulator and we were back to our stable behavior - no software keyboard. If you're having similar problems, try out this solution for running tests on Android 5.1.1 and above.

Monday, February 29, 2016

The Android API provides a useful way for developers to collect and view system debug output. The tool, Logcat, contains stack traces from application crashes, as well as custom messages that developers can provide in their code. Every log message has an associated "tag" field, which is the main focus of this post.

Conventionally, developers will define a constant string field called TAG near the top of their code files. There are a variety of ways to define this field, but the main choices are usually:

This means that #1 & #2 have an extremely high chance of truncation right off the bat. Having a tag of "com.example.android.in" instead of "Foo isn't very useful.

Even with #3, if the class name is long you could still end up with a truncated log tag. With #4, you can customize your tag to ensure it's under 23 characters even if your class name is longer.

As of Build Tools 21.0.3, there is a lint check for this, but the lint check can only detect tags created with string literals, as in option #4.

If you're using Proguard to obfuscate your app, then class name Foo gets obfuscated and your tag becomes something like "a" instead of "Foo" if you're using #1-#3.

#4 is what Google uses inside the AOSP codebase (though I'm not aware of any justification for Google on why they do this)

The main downside of option #4 is that the tag won't automatically be updated if you refactor the class name. However, since TAG is generally the first line in a class, it's likely that the discrepancy will be noticed either by the person doing the refactoring or someone who reviews the code change.

Another useful tip for using option #4 is to update your new class template in Android Studio to automatically create your tag for you. Just go to Preferences -> Editor -> File and Code Templates -> Class and change the template to the following:

This will automatically create a TAG field with the same name as your class file, and then you can customize it if needed.

Happy logging!

Friday, January 1, 2016

After recently wrapping up the initial release of the new LinkedIn app for Android & iOS, I took a long-awaited break over the holidays to unwind and refresh. I started taking a look at all the stuff I'd been putting off for the last few months...and realized I needed a better way to stay on top of all this.

I had over 50 constantly open Chrome tabs with things I needed to eventually follow up on. I've tried using bookmarks in the past, but I've found that if it's not in my face somehow, I'll just forget about it. I also had a collection of random files in my Mac's Downloads folder, because I needed to do something with them someday and needed to have them available for that eventuality. I also had about 80 emails waiting in my Gmail inbox for some kind of follow up or future project that would require replying to the thread. There was an assortment of notes in Google Keep mentioning ideas I'd had and wanted to look into once things slowed down. And of course, there was always the mental list of stuff I needed or wanted to do that never made it into any of these "systems"...I'd just remember them periodically and wince that I hadn't done them yet before forgetting them for a few more weeks.

This was not good in a number of ways...it was stress-inducing, for one. It also made it hard to pick something to work on when I actually found myself with some free time...where should I look for a new task? And perhaps the most annoying problem....all those open Chrome tabs slowed down my computer!

So yesterday I downloaded the 14 day trial of OmniFocus. I've heard nothing but praise about this Mac app from The Omni Group so I thought I'd see if it could help me get organized. I won't go into a lot of detail here, because I'm sure others have covered the program much more thoroughly, but so far I'm hopeful that this will help keep things organized. I spent an hour or two doing a brain dump into OmniFocus's "Inbox", converting all my mental todos into "actions", and then slowly converting my open Chrome tabs and emails into actions that had a link back to the referencing web page or email thread.

So far my biggest win is being able to close all the tabs I perpetually had open because I needed to check them for something once per day. I replaced them with a set of repeating tasks to check the pages every morning when I come into work. We'll see how this system works once I get back into the swing of things this month, but for now I'm happy with just my email, calendar, and scrum board tabs open in Chrome.

If I continue to like OmniFocus as much as it seems like I will, I'll definitely be paying the relatively cheap $39.99 price tag for their Mac app. I'd encourage you to check it out if you're looking for a good way to organize your todo list and other tasks.

Wednesday, November 25, 2015

Yesterday was the last day of the Android Dev Summit conference by Google. The conference was packed with deep-dive tech talks on some of the most relevant topics to Android devs today, including Gradle build optimizations, performance testing tools, and data binding tips. I left the conference with a long list of things I can change to improve the performance of my build and tests, and be ready for new tooling features from Google like Instant Run.The full list of improvements will take a while to implement, but there were three simple fixes I was able to make in a day that had a dramatic impact on the performance of my builds.------------------------------------------------------------------------

#1 Don't add timestamps, etc to BuildConfig fields in debug builds

It's very convenient to add extra fields to your BuildConfig file. My app adds the git commit sha and the build time so we can display this information inside the app on the developer settings screen. Unfortunately, both of these things change very frequently; timestamp obviously changes every time you build. This means the BuildConfig file needs to be regenerated on every build, and any files that depend on it (probably many files in the app) need to be recompiled as well, essentially killing the benefits of incremental builds.

The fix for this one is to only generate these fields in release builds. In debug fields, we just set them to the constant string "DEBUG".

#2 Use the official api for generating code

Version 0.7.0 of the Android Gradle plugin added two api methods: registerJavaGeneratingTask() and registerResGeneratingTask(). These let you register a custom gradle task that will generate code, and will automatically set up the code to be generated at the proper time and indexed & recognized as source by the IDE.

My app used a custom task to generate model files from API data schemas, but we were manually inserting it into the build pipeline without using the official api. The downside of this is that the task was never recognized as "up to date", which forced all models (and all classes that depended on those models) to be recompiled on every build, removing the benefits of incremental builds. Before switching to the official API, running "gradle assembleGoogleDebug" consistently took ~2 minutes, even if there were no code changes. After switching, running the task with no code changes takes ~16 seconds.

#3Fix dependency conflicts between different configurations

This one isn't a performance optimization, but rather a way to increase the maintainability of your build script. I ran into this issue a long time ago on my app; Android testing works by loading your app apk and your test apk in the same classpath. If the two configurations depend on different versions of the same library, you'll run into crashes at runtime due to the conflict. The most frequent offender is the support annotations jar, since almost every modern Android library depends on it. Gradle already forces your build to use the same version of shared transitive dependencies, but this only works within the same configuration.

I fixed this previously using Gradle's resolutionStrategyoption to force all configurations to resolve to the same version of a dependency, but this isn't a great solution because it requires you to hardcode the version of your dependency in a second location and there's a strong chance you'll forget to update it. The better option is to just add the dependency normally, but add it to both your prod and test configurations.

This is just the low hanging fruit that I was able to optimize in a day. Going forward, I'll be following up on several other lessons learned from the conference and hopefully improving build times and incremental compilation times even further. Be sure to check out the session videos from the Android Dev Summit conference if you want to learn more!