Jetifier Reverse Mode
If you’re working with Android nowadays, you’ll probably be familiar with Android X, the major package renaming of the Android Support library announced at Google I/O 2018. It’s time to migrate!
Although, you might be in the situation where you can’t yet migrate. Either because you’re working on a big project (that you’re not fully responsible for) or just because you love procrastinating. One of the tools can come handy in this case is Jetifier in reverse mode.
AndroidX
Google finally decided to clean up and re-organize the good old Support Library (launched in March 2011!). Historically, classes in the Support Library used to have packages like android.support.v4.app.
.
The .v4
in the package meant that the code provided was supposed to be backward compatible for API level 4 and higher. Given that now we’re at API level 28, having a .v4
in the package name doesn’t add much value (it mostly creates confusion).
All the packages in the Support Library got standardized under the androidx.
package. So for examples android.support.v4.app.Fragment
will be androidx.fragment.app.Fragment
. The list with all the mapping is pretty long. You need to take care that everything in your codebase is migrated to use the new packages.
Ideally, this process should be smooth using the Refactor -> Migrate to AndroidX tool inside Android Studio to do all the job. Unfortunately is not always working, and you probably need to do some manual adjustments, especially on bigger projects.
The last thing you need to do is add this line to your gradle.properties
android.useAndroidX=true
What about your dependencies? They need to be migrated as well. Here is where Jetifier comes into play.
Jetifier
If you migrated to AndroidX and you’re using a library that is not migrated yet, you will probably have some problems. Let’s say that you’re using a library that provides you a LibraryFragment
that is a subclass of android.support.v4.app.Fragment
. You will not be able to use it as your application is expecting a androidx.fragment.app.Fragment
subclass.
Jetifier is a tool provided by Google to help you solve exactly this kind of issue. Jetifiery will convert the bytecode of your dependencies (aka jetify) to use the AndroidX classes following the mapping.
The enable Jetifer you just need to add this line to your gradle.properties
android.useJetifier=true
and the dependencies will be converted automagically during the build.
Alternatively, you can download the standalone version to convert single artifacts directly from your command line.
Thanks to this, you’ll be able to use either migrated or not migrated dependencies in your project.
What about if you’re maintaining an Android Library?
The library developer’s point of view
If you’re a library developer, you probably asked yourself: Should I migrate my library to AndroidX?. The risk here is that if you migrate to AndroidX, users that haven’t yet migrated can’t use your library.
You basically have 3 options:
Don’t migrate.
The laziest alternative is to don’t migrate. Apps that are using your library and are not migrated, will keep on working as usual. Apps that are migrated instead will jetify your compiled code to make it compatible with their codebase.
Maintain two variants of your library.
You can maintain two variants/flavors of your app. The -androidx
variant will be the migrated instance and you should point out in the documentation which artifacts each user should use.
This is probably the safest approach as users won’t need Jetifier at all, avoiding all the potential bugs of this tool. At the same time is probably the most costly solution as you need to take care of two artifacts and you might end up in some code duplication.
Migrate.
Do a major release of your library and announce that from version X.
you will be supporting only AndroidX. Don’t forget to point out that the in your Readme file (e.g. pinning the latest version of the library that is not migrated to AndroidX).
This is the approach I used for a library I maintain: AppIntro. We decided that from 5.x
the library will support only AndroidX.
People that want to use the latest version of the library should either update to AndroidX or use Jetifier in Reverse Mode.
Reverse Mode
Jetifier in reverse mode can be used to de-Jetify the bytecode of a library, converting the new AndroidX packages to the old one.
Unfortunately, the reverse mode is not integrated into the Android Gradle Plugin and there is no property to set to enable it. The only way to run it is to use the standalone version.
Luckily the command line jetifier is not so hard to use:
$ ./bin/jetifier-standalone
ERROR: [Main] Missing required options: i, o
usage: Jetifier (standalone)
-c,--config <arg> Input config path (otherwise
default is used)
-i,--input <arg> Input library path (jar, aar, zip)
-l,--log <arg> Logging level. Values: error,
warning (default), info, verbose
-o,--output <arg> Output file path
-r,--reversed Run reversed process
(de-jetification)
-rebuildTopOfTree,--rebuildTopOfTree Rebuild the zip of maven
distribution according to the
generated pom file.If set, all
libraries being rewritten are
assumed to be part of Support
Library. Not needed for
jetification.
-s,--strict Don't fallback in case rules are
missing and throw errors instead
We need the -r
option to de-jetifiy. Let’s try to use it with the AppIntro artifact:
./bin/jetifier-standalone -r -i AppIntro-v5.1.0.aar -o deJetified.aar
That’s it! Your .aar
is now de-jetified and can be integrated into your app.
To actually use it you also need the .pom
file where all the library dependencies are listed.
For AppIntro, for example, we have those dependencies in the pom file:
<dependencies>
<dependency>
<groupId>androidx.appcompat</groupId>
<artifactId>appcompat</artifactId>
<version>1.0.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>androidx.annotation</groupId>
<artifactId>annotation</artifactId>
<version>1.0.0</version>
<scope>compile</scope>
</dependency>
...
</dependencies>
You can either update them manually or let Jetifier do the job. You can’t pass the .pom
directly to the command line tool, but you have to create a .zip
file where you can put all the content of the maven repository: .aar
, .pom
, -sources.jar
, etc.
Using this tool, you can probably encounter this warning:
WARNING: [ProGuardTypesMap] Conflict: [ProGuardType(value=androidx/preference/{any})] -> (ProGuardType(value=android/support/v14/preference/{any}), ProGuardType(value=android/support/v7/preference/{any}))
Here Jetifier complains because it’s not able to reverse the mapping for androidx.preference.
in the ProGuard file. The problem is due to both android.support.v14.preference.
and android.support.v7.preference.
being migrated to the same AndroidX package, so the mapping is ambiguous.
You can fix this warning by passing a custom mapping file to Jetifier with the -c
option. You can use this as a starting point and customize it as you wish. The warning is raised by those lines. You can remove one of the two rules in the config file to remove the warning.
"proGuardMap": {
"rules": {
...
"android/support/v7/preference/{any}": [
"androidx/preference/{any}"
],
"android/support/v14/preference/{any}": [
"androidx/preference/{any}"
],
...
Now you have your de-jefitifed artifact ready, that can be added as a dependency to your project.
Happy (de)jetification! 🕺
Leave a comment