The Reality of Migrating to AndroidX

This year, Google rebranded the support Android libraries to Android Jetpack (aka AndroidX).

Particularly pertinent to developers was how they repackaged all the libraries. Not only have their maven coordinates changed, but the package names for every class has changed as well. For example, android.support.v7.app.AppCompatActivity is now androidx.appcompat.app.AppCompatActivity.

The old artifacts and package names were a confusing mess, so in the long run this is a great move. In the short-term, though, upgrading requires extensive changes to your codebase.

Theoretically, this migration should be as easy as running the "Migrate to AndroidX" tool in Android Studio. If that works for your app, then fantastic! Stop reading this article now!

Unfortunately, we found it to be far more of a hassle than that. Here's how we actually accomplished our migration.

Migration Preparation

Before you begin to flirt with the idea of migrating to AndroidX, you should be up-to-date on the latest versions of all your current support library artifacts. That way the leap from old to new artifacts should only require changing package names.

For the most part, that means being on version v28.0.0 of the support libraries. However, some support libraries use other versioning schemes: for example, we had to upgrade Android KTX to v0.3 and the Room persistence library to v1.1.1.

Doing this preparation upfront is great for revealing parts of your codebase that require older versions of the support library and must be updated. For example, we discovered that SqlDelight required an old version of the Room persistence library. In the end, we asked for (and received) a version of SqlDelight that made the transition easier by supporting the latest persistence library (thanks, Square!).

Migration Failure

Once your dependency ducks are in a row, the next step is to try Android Studio's "Migrate to AndroidX" tool. Unfortunately, it failed our codebase in multiple ways:

  • The maven coordinates for the AndroidX artifacts were often out-of-date (referring to alpha builds instead of the latest stable builds).

  • It did not actually find/replace all the package references that needed updating.

  • It made extraneous changes to our codebase - it should've only touched import statements, but instead it unnecessarily modified other references as well.

Fixing the maven coordinates was easy - either look up the most recent version on Google's repository, or use gradle-versions-plugin to automatically detect the latest artifacts.

The more daunting task was ensuring we had actually converted all the package names. Luckily, Google provides a csv file mapping old to new package names that we used in a small script to convert the codebase. This script took a couple minutes to run, but it allowed us to consistently convert the codebase to the new package names.

Unfortunately, the class mapping CSV is incomplete. For example, it's missing most of the design library class mappings (i.e. classes in android.support.design.widget). I had to add a few rows to my own CSV to get it to convert everything.

Jetifier Limitations

The jetifier is a handy tool that automatically migrates your dependencies to AndroidX at build time. Without it, you'd need every dependency you use to have an AndroidX version before you can migrate, and that probably won't happen for some time.

There's an important limitation to note: the jetifier only works on packaged artifacts. It does not work on your source code, which you are expected to update yourself.

Unfortunately, your app may have code which is neither a packaged artifact nor source code you control: generated code. You neither write nor maintain it, but it's compiled as if it's your own source code.

If the generated code refers to old artifacts you can run into trouble. Take, for example, Butter Knife. It generates classes that contains references to support annotations (e.g. android.support.annotation.UiThread). These imports cannot be jetified to AndroidX (since it's source code), so you'll get compiler errors instead.

In the case of generated code, you must update your dependency so it generates AndroidX-compatible code.

This was a problem for us with two code generators - Butter Knife and SqlDelight. Thankfully, both have versions that support AndroidX now (many thanks to Square for making AndroidX-friendly artifacts).

Merge Nightmares

Given all of the above, you should not be surprised to learn it took me a decent amount of time to upgrade our codebase to AndroidX.

The time it took caused its own problems, since the other developers on my team were still constantly changing the codebase. Every change they made that modified import statements would cause painful merge conflicts with my ongoing AndroidX branch. Maintaining that branch by hand would've been a gigantic pain.

This issue was the second reason I wrote the migration script. Whenever I rebased my AndroidX branch onto the latest master, I would simply re-run the script rather than trying to deal with the merge. I saved a whole bundle of effort this way.

Conclusion

The support library artifacts are being deprecated and all future development is going into AndroidX, so there's no avoiding this migration. Hopefully, though, these tips will give you a clearer transition path.