Over a year ago the support library development team decided to split the monolithic
support-v4 library up into several sub-libraries. This update, in version 24.2.0, meant that an app no longer needed to depend on the entire
support-v4 library and could instead depend on only those sub libraries that it needed.
Until now I have yet to implement this update on any of my work or personal projects. This week as part of a general dependency audit, we decided to split up our
In determining which “new” support libraries our project relies on, I could have inspected every
import android.support.v4.app.xyz statement in our app. This would be a tedious process. Instead, I decided to remove the
support-v4 dependency entirely, build our app, and let Gradle complain about missing dependencies. I would then add in each new support library as needed.
However when I removed the
support-v4 dependency and built our project it compiled fine. I had a momentary brain fart. “We’re using fragments. We are using the support Fragment right?” Sure enough, our project had plenty of dependencies on
support.v4 package but compiled fine.
I then realized that the
support-v4 dependency must be supplied as a transitive dependency. Sure enough, running
./gradlew app:dependencies revealed that quite a few of our dependencies were themselves depending on
+--- com.android.support:transition:25.3.1 | ... | \--- com.android.support:support-v4:25.3.1 (*) +--- com.android.databinding:library:1.3.1 | +--- com.android.support:support-v4:21.0.3 -> 25.3.1 +--- com.google.firebase:firebase-core:10.2.1 | \--- com.google.firebase:firebase-analytics:10.2.1 | +--- com.google.android.gms:play-services-basement:10.2.1 | | \--- com.android.support:support-v4:24.0.0 -> 25.3.1 (*) +--- com.android.support:appcompat-v7:25.3.1 | ... | +--- com.android.support:support-v4:25.3.1 (*) +--- com.facebook.android:facebook-android-sdk:4.20.0 | +--- com.android.support:support-v4:25.0.0 -> 25.3.1 (*) +--- com.zendesk:sdk:188.8.131.52 | ... | +--- com.android.support:support-v4:25.1.0 -> 25.3.1 (*) | ... | +--- com.android.support:design:25.1.0 | | +--- com.android.support:support-v4:25.1.0 -> 25.3.1 (*)
At this point it occurred to me that unless our dependencies fixed their
support-v4 dependencies, there wasn’t much use in fixing things ourselves. The problem with this thinking, though, is that it can lead to a Mexican standoff: each library pointing at some other library waiting on them to upgrade. This was already happening with our Zendesk dependency**.
The solution though would be to prepare our app and politely pressure our dependencies to upgrade their own dependencies. After all, the more people “waiting” for a particular library to update, the more pressure on that library author to cleanup their dependencies.
The easiest solution would be to depend on all of the new libraries (whether they were top level dependencies or not) to ensure every library is using the same version number. We could then remove individual libraries as our dependencies defined only those that they needed.
implementation com.android.support:support-compat:... implementation com.android.support:support-core-utils:... implementation com.android.support:support-core-ui:... implementation com.android.support:support-media-compat:... implementation com.android.support:support-fragment:...
It’s at this point that I read the entire documentation (RTFM, stupid) and saw that
support-fragments depends on the remaining:
support-media-compat. Well crap. The main reason we use
support-v4 is for support Fragments. So if
support-fragment depends on all of the other new support libs, what’s the point of being explicit about our dependencies?
Then the question came to me: If we are relying on
support-fragment to provide other things like
support-compat, what’s wrong with relying on
appcompat-v7 to provide us
support-fragments in the first place?
There is some good discussion about relying on transitive dependencies on StackExchange. In the case of
support-fragment though, I think it’s not only acceptable to rely on transitive dependency, it should be encouraged for two reasons:
- They are maintained by the same authors.
- The authors “provide” these transitive dependencies explicitly.
I think the first point is pretty self explanatory. The
appcompat-v7 lib stays in lock step version number with
support-fragment. You don’t have to worry about them getting out of sync and needing to force
support-fragment library to latest version by including it as a top-level dependency.
The second point is a bit more subtle. With the inclusion of the new Java Library plugin configurations the support library authors are forced to decide whether or not to expose their dependencies to others. They have chosen to use the
api configuration. If they wanted to limit exposure of those dependencies, they could have chosen the
implementation configuration. I believe this is neither an accident, nor is it “dependency leaking”. Instead I choose to believe that they are purposefully providing us with access to
support-fragments (among others).
So if the solution for us is to remove the
support-v4 dependency and keep the
appcompat-v7 dependency, what was all of this exploring for? Hopefully I can distill all of this thinking into a quick guide.
But wait, what about Proguard?
Yes, using Proguard can solve the support-v4 “monolith” problem. But I don’t think Proguard should remove your obligation to write clean code and remove useless code. I think there is already too much bad library design that hides behind Proguard.
If you are early in app development:
- If you need code included in
appcompat-v7, then use it. You don’t need to add
support-v4or any of the new libraries.
- If you don’t need anything from
appcompat-v7except one of its dependencies, use that dependency directly, even if your minSdk is 7+.
- If your minSdk is 4+ use
support-fragment. You are using Fragments after all, right?
- If you are building an app with minSdk 4+ and you are somehow not using Fragments, then use only those new libraries you need:
If you are building a library:
- Declare all code that you use as top-level dependencies. For example, if you use code from
appcompat-v7AND support Fragments, then you should declare dependencies on both
support-fragment. Unlike app developers, you are in danger of having your dependency versions changed by your users. Even though you declare a dependency on
appcompat-v7version X, there is nothing stopping your users from “swapping” to version Y by declaring a top level dependency on version Y themselves. If a later version of
support-fragmentdependency (however unlikely), your library would no longer work for that user.
If you have an existing app:
- If you are already running Proguard, then it’s not worth your time to change anything. Otherwise…
- If you already depend on
appcompat-v7, then use it. You can remove any dependency on
support-v4, or the new libraries if you’ve already switched.
- If you don’t need anything from
appcompat-v7except one of its dependencies, remove the
appcompat-v7dependency and add dependencies for one or more of the new libraries, even if your minSdk is 7+.
- If your minSdk is 4+ and you use support fragments, you can switch from
support-fragment, but you won’t see any improvement.
- If your minSdk is 4+ and you are somehow not using Fragments, then use only those new libraries you need. You can remove
support-v4and add a subset of
This is where things get tricky. For any transitive dependencies that don’t match the version number you are using, you’ll need to add those dependencies as top-level dependencies as well. Otherwise you risk the “All com.android.support libraries must use the exact same version specification” error. For example if you depend directly on
support-compat:26.0.0 and library X, which in-turn depends on
support-v4:25.0.0, you will need to add a top-level dependency for
However, if library X depends on
support-v4:24.1.1 or older you will have to maintain your dependency on
support-v4:26.0.0. You won’t be able to depend on
support-fragment:26.0.0 because versions of
support-v4 24.1.1 and older is NOT just a collection of the new support libraries. Overriding with “support-fragment:26.0.0
will not overridesupport-v4:24.1.1` version.
I hope this overly-complex dive into support library dependencies keeps you from being stressed about the importance of using the new sub-divided support libraries.
**To be fair to Zendesk, they reached out to me right away with an update.