Illustration by Virginia Poltrack

Cross-stitching Plaid and AndroidX

An AndroidX migration guide

Plaid is a fun Android app that showcases Material Design and a rich, interactive UI. Recently the app underwent a refresh by applying modern day Android app development techniques. To read more about the app and the vision for the redesign, check out Restitching Plaid.

Like most Android apps, Plaid relies on the Android Support Library which provides backwards compatibility of new Android features to phones running older versions of the OS. In September of 2018, the last version of the Support Library (28.0.0) was released. The libraries that shipped with the Support Library have been migrated to AndroidX (with the exception of the Design library which has migrated to Material Components for Android) and all new development of these libraries occur in AndroidX. Thus, the only way to receive bug fixes, new features, and other library updates was to migrate Plaid to AndroidX.

What is AndroidX?

At Google I/O 2018, the Android team announced AndroidX. It is the open-source project that the Android team uses to develop, test, package, version, and release libraries within Jetpack. Like the Support Library, each AndroidX library ships separately from the Android OS and provides backwards-compatibility across Android releases. It is a major improvement and full replacement of the Support Library.

Read on to learn about how we prepared our code for the migration process and performed the migration itself.

Prepare for migration

I highly suggest performing the migration on a version controlled branch. This way you can incrementally address any migration issues that may arise along with separating out each change for analysis. You can see our conversion progress in this Pull Request and follow along via the commit links below. Additionally, Android Studio offers an option to backup the project prior to starting the migration.

As with any large-scale code refactoring, it’s preferable to minimize merges to your main development branch while migrating to AndroidX to avoid merge conflicts. While this may not be feasible for other apps, our team was able to temporarily pause commits to our master branch to aid in the migration. It’s also essential to migrate the entire app at once, as a partial migration — using both AndroidX and the Support Libraries — will cause failures.

Finally, read the tips on Migrating to AndroidX on developer.android.com. Now let’s begin!

Identify dependencies

Before you begin, the most important piece of advice on code prep is:

Ensure that the dependencies that you’re using are compatible with AndroidX

Libraries that depend on an older version of the support library may not be compatible with AndroidX, resulting in the likelihood that your app will not compile after the AndroidX migration. One way to find out if any of your app’s dependencies are incompatible is to visit each dependency’s project site. A more straightforward approach is to begin the migration and examine the errors that may come up.

For Plaid, we were using an older version (4.7.1) of the Glide image loading library that wasn’t compatible with AndroidX. This caused a code generation issue after migration that left the app unbuildable (here is a similar issue logged in the Glide project). We upgraded to version 4.8.0 (commit) which added support for AndroidX annotations prior to starting the migration.

On that note, update to the latest versions of your dependencies if possible. This is especially a good idea for the Support Library, as upgrading to 28.0.0 (the final version) will make the migration smoother.

Refactor with Android Studio

For the migration process, we used the refactoring built into Android Studio 3.2.1. The AndroidX migration tool is located under the Refactor menu > Migrate to AndroidX. This will migrate your entire project, including all of the modules.

The refactor preview window after running the AndroidX refactor tool

If you don’t use Android Studio or prefer to use another tool for migration, refer to the pages on Artifact and Class mappings. These are also available in CSV format.

The AndroidX migration tool in Android Studio is a great resource for migrating to AndroidX. The tool is continually improving, so if you encounter issues or would like to see a feature, please file a ticket on the Google issue tracker.

Migrate the app

Change the minimum amount code in order to have the app run again.

After running the AndroidX migration tool, a large amount of code changed, but the project did not build. At this point, we only did the minimum amount of work to get the app running again.

This approach helps divide the process into manageable steps. We left tasks such as fixing import order, extracting dependency variables, and reducing full classpath usage for a clean up pass later.

One of the first errors that came up was a duplicate class — in this case, PathSegment:

Execution failed for task ':app:transformDexArchiveWithExternalLibsDexMergerForDebug'.
> com.android.builder.dexing.DexArchiveMergerException: Error while merging dex archives:
Learn how to resolve the issue at https://developer.android.com/studio/build/dependencies#duplicate_classes.
Program type already present: androidx.core.graphics.PathSegment

This was an error in the migration tool that generated an incorrect dependency (androidx.core:core-ktx:0.3). We manually updated (commit) to the correct one (androidx.core:core-ktx:1.0.0). This bug has since been fixed and is available in Android Studio 3.3 Canary 9 and later. We wanted to point this out as you may encounter similar issues during your migration.

Next, the Palette API changed and is now nullable. To temporarily sidestep this (commit), we added !! (the not-null assertion operator).

We then ran into an error where plusAssign was missing. This overload was removed in 1.0.0. plusAssign usage was temporarily commented out (commit). Later in the article we’ll explore the permanent solutions for Palette and plusAssign.

The app now runs! Next, time to clean up the code.

Clean up code

The app was running, but our Continuous Integration was reporting errors on commits:

Execution failed for task ':designernews:checkDebugAndroidTestClasspath'.
> Conflict with dependency 'androidx.arch.core:core-runtime' in project ':designernews'. 
Resolved versions for runtime classpath (2.0.0) and compile classpath (2.0.1-alpha01) differ. This can lead to runtime crashes. 
To resolve this issue follow advice at https://developer.android.com/studio/build/gradle-tips#configure-project-wide-properties.
Alternatively, you can try to fix the problem by adding this snippet to /.../plaid/designernews/build.gradle:
  dependencies {
implementation("androidx.arch.core:core-runtime:2.0.1-alpha01")
}

We followed the advice in the test logs and added the missing dependencies block (commit).

We also took the opportunity to update our Gradle, Gradle wrapper, and Kotlin versions (commit). Android Studio prompted us to install version 28.0.3 of build tools, which we complied. We ran into issues with Gradle 3.3.0-alpha13, which was resolved by downgrading back to 3.3.0-alpha08.

One drawback of the migration tool is that if you use variables in your dependencies versions, it will inline them for you. We re-extracted the versions out of build.gradle files (commit).

We mentioned above that we put in temporary solutions for plusAssign and Palette after running the AndroidX migration tool. Now, we re-added the plusAssign function and associated tests (commit) by porting it from a previous version of the AndroidX library and uncommenting the code from above. Also, we updated the Palette parameters to be nullable (commit), which removed the need to use !!.

Also, the auto conversion might change some classes to use their full classpath. It’s a good idea to make minor manual corrections. As part of the cleanup we removed the full classpath and re-added the relevant imports as necessary.

Finally, some minor test modifications were added to work around dependency conflicts during testing (commit) and Room tests (commit). At this point our project is fully converted and all of our tests are passing.

Ending the thread

Despite running into a few roadblocks, the AndroidX migration went smoothly. The issues mostly involved incorrectly converted dependencies or classes, and API changes in the new libraries. Fortunately, these were all relatively easy to resolve. Plaid is now ready to wear again!