Short Form: What are some ways of organizing the code/POM for an AAR, such that apps using that AAR only have dependencies they actually need?
Long Form:
Suppose we have an app that depends upon an Android library project packaged as an AAR (L).
L contains a mix of classes, and any given app (like A) will only use a subset of those classes. For example:
L may contain
Fragment
implementations for native API Level 11 fragments, the backported fragments, and ActionBarSherlock-flavored fragmentsL may contain
Activity
implementations for regular activities,FragmentActivity
,ActionBarActivity
, and ActionBarSherlock-flavored activitiesL may raise events via
LocalBroadcastManager
, Square's Otto, and greenrobot's EventBusAnd so on
These cases have two main commonalities, as I see it:
Apps will usually only care about some of the classes. For example, an app that uses Otto will not care about code that references greenrobot's EventBus, or an app that uses
ActionBarActivity
will not care about ActionBarSherlock.If the AAR is an artifact in a repo, apps will not care about all possible upstream dependencies that is needed to build the AAR. For example, an app that is using native API Level 11 fragments will not need
support-v4
oractionbarsherlock
, even though the AAR itself needs them to build the AAR
If we were to go with JARs instead of AARs and dump dependency management, this is fairly straightforward. Building the JAR would have compile-time dependencies (e.g., support-v4
). However, apps using that JAR could skip those dependencies, and so long as those apps do not use classes that truly need those dependencies, life is good.
However, I am having difficulty in seeing how to accomplish the same thing with AARs and Maven artifacts specified in a build.gradle
file. If L has a dependencies
block referencing the upstream dependencies, apps will in turn download those dependencies transitively when the apps depend upon L.
One solution that I am fairly sure will work is to split L into several libraries. For example, using the fragment scenario, we could have:
L1, which contains the implementation for the native API Level 11 version of fragments, plus any common code needed for other scenarios. This library would have no upstream dependencies.
L2, which contains the implementation that uses the Android Support package's backport of fragments. L2 would have dependencies on L1 and on
support-v4
.L3, which contains the implementation that uses Sherlock-flavored fragments. L3 would have dependencies on L1 and
actionbarsherlock
.
Then, an app would choose whether to depend upon L1, L2, or L3, and therefore would only get the necessary upstream dependencies (if any).
My question is: is that the best solution? Or is there something else in the world of Gradle for Android, AARs, and Maven-style artifacts that would allow apps to depend upon a single L? I am concerned about possible combinatoric explosions of libraries to handle a varied mix of upstream dependencies. I am also concerned about oddball apps that actually do need multiple implementations and whether or not we can reliably specify the dependencies for those (e.g., an app depending on both L1 and L2, because that's what that app's author thinks that app needs).
I know that there are ways for an app to block exclude dependencies (see Joseph Earl's answer for the syntax), so an app could depend upon L but then block the actionbarsherlock
upstream dependency if it is not needed. While that could work, for cases where I'm the author of L, I'd rather go the L1/L2/L3 approach, as that seems cleaner.
Any other suggestions?