How does gradle resolve duplicate dependencies with different versions like Retrofit? - android

Suppose, I want to create android library with Retrofit 2.0.0 dependency. What if developer, that will use my library also would have dependency with Retrofit lower version (i.e. 1.9.0) in his project , how gradle will resolve this ? Also, Is it bad approach to use libraries like Retrofit in my own library to simplify code?

Gradle has a number of dependency resolution strategies, you can take a look at the dependency management part of the official Gradle user guide. Especially at 52.2.3. Resolve version conflicts.
The most common strategies are Newest:
Newest: The newest version of the dependency is used. This is Gradle's default strategy, and is often an appropriate choice as long as versions are backwards-compatible.
And Fail:
Fail: A version conflict results in a build failure. This strategy requires all version conflicts to be resolved explicitly in the build script. See ResolutionStrategy for details on how to explicitly choose a particular version.
So, the behaviour of dependency resolution in gradle build script depend on what resolution strategy is used. In most cases, the newer version will be used.
The questions like "Is it bad approach to use libraries like Retrofit in my own library to simplify code?" are mostly opinion based, as for me. You are able to use anything you need, especially if you can't do something without it or it'll take too long to implement it yourself. Tools like a Gradle are made to simplify your life and try to solve most of the problems you can catch with transitive dependencies and you don't have to worry much about it. If you are not creating some widely used library, which should run everywhere no doubt.

Related

How to get new version warning in Build.gradle with pre-defined dependencies?

When you reference a dependency directly in gradle, IntelliJ gives a lint warning if there is a newer version. i.e. implementation 'com.library:name:1.0.0' gives a warning that version 1.0.1 is available.
If, however, you reference the library via a gradle ext. property, it does not give that warning. i.e. implementation deps.library.name does not give a warning. Is there any way I can still get that warning while referencing previously defined libraries?
In my case I have several modules that re-use a lot of the same libraries, so it's important to be able to define all the libraries and their versions in one place and then just refer to them from each module that needs them. But this causes me to no longer be told by the IDE when there are new version. How can I fix this?
This is actually a feature of Android Studio and indeed it does not support all the ways a dependency can be defined in Gradle.
An equivalent can be achieved with the plugin gradle-versions-plugin through its dependencyUpdates task.

Why compile- and run-time dependency is important?

I know the difference between the two as discussed here.
As Android developer,
Why I should care about this?
In gradle, why should I use compileOnly vs implementation/api?
Why I should care about this?
To make your apps build but not ship with unnecessary stuff.
In gradle, why should I use compileOnly vs implementation/api?
The documentation for compileOnly gives one use case as an example:
Gradle adds the dependency to the compilation classpath only (it is not added to the build output). This is useful when you're creating an Android library module and you need the dependency during compilation, but it's optional to have present at runtime. That is, if you use this configuration, then your library module must include a runtime condition to check whether the dependency is available, and then gracefully change its behavior so it can still function if it's not provided. This helps reduce the size of the final APK by not adding transient dependencies that aren't critical. This configuration behaves just like provided (which is now deprecated).
source
For example, consider a push messaging library that supports both Firebase FCM and Amazon ADM but does not require either. It would unnecessarily bloat apps if it would ship with both as transitive dependencies. With compileOnly the library can still be built. Developers using the library can select which dependencies to actually use.
Another example could be compile-time annotations that do not need to ship with the application.

How does Gradle choose between more than 1 versions of the same library in a gradle tree?

Let say I have added Facebook and Twitter dependencies in my app.
com.facebook.android:facebook-android-sdk:4.22.1
com.twitter.sdk.android:twitter:2.1.0
When i look at Gradle tree, They come up with bunch of other transitive dependencies.
Now If Facebook uses com.android.support:support-annotations:24.1.1 and twitter uses com.android.support:support-annotations:25.0.3
Then which version gradle will use.
In gradle tree, It shows -> in front of older version of dependency. I learnt that this means gradle will use the newer version, and will not use the older version.
But this can be a problem, because some libraries are meant to run on the specific versions, and i have faced this problem.
In one of article i found out how npm manages these dependencies conflicts, but i am still unsure how gradle will manage between different version of same library.
You can't have different versions of the same library inside an apk.
As you mentioned, by default, Gradle puts the newest one to the build. If you want to specify a concrete version of the library that should be used in your project, you can add a direct compile (or implementation / api for Android Gradle Plugin v3+) statement with a required version to get one.
Also, you can force version using a special syntax, but it can lead to some problems later. You can find more information about version conflicts resolution in this post

Correct way to resolve error: all support libraries must use the exact same version

I'm using Tommy Buonomo's excellent library (here on GitHub and this SO question where he introduced it) to add a position indicator to a ViewPager, however I am getting a gradle error about support libraries versions which raises a bigger question for me.
The specific error is:
Error:(39, 20) All com.android.support libraries must use the exact same version specification (mixing versions can lead to runtime crashes). Found versions 27.0.0, 25.3.1. Examples include 'com.android.support:support-compat:27.0.0' and 'com.android.support:animated-vector-drawable:25.3.1'
(Note that this is flagged as an Error by Android Studio BUT the app still compiles and runs.)
The bigger question this raises is how do you properly handle conflicts in support library versions like this?
I have added the library simply by adding:
compile 'com.tbuonomo.andrui:viewpagerdotsindicator:1.0.1'
to my dependencies in my app level build.gradle and as I say everything is working well except for the error.
I'd like to keep my app as error and warning free as possible so I'd like to understand what I can do to resolve this.
This is the first time I have used a third party library in one of my projects so this is the first time I have encountered this specific issue.
So, some questions.
Must a library developer create various versions of their library that target specific other support library versions or can they simply create one library that supports all versions up to a specific version? (It would seem impractical to have to create specific versions as you don't know what versions a library user is going to be using).
Is a user usually able to change the version of other support libraries that the third-party library calls? I cannot seem to find anywhere in the external libraries section of the project tree that would let me do this for Tommy's library so I assume many libraries are like this.
Is the only option to wait for the library developer to create a new version that uses the same API level support libraries that my app uses? (Or to change the support library version that my app uses).
Would another option be to fork the library on github and change the support library versions it uses in the new fork? This then raises the issue of learning how to compile and use libraries and to republish them for others to use (maybe as a pull request to the original author so that they could incorporate the changes - this would be most in the spirit of Tommy having released the library anyway) - and would also require an understanding of all of questions 1-3.
I feel like having got this error I have opened a can of worms here, but I would like to understand more about how this should be fixed if I am to do things properly.
(FYI, I am building my app with compile and target SDK v27 in order to follow best practice of targetting latest API level. Tommy's library already has a pull request to support API v26 which is awaiting action)
You can force the dependencies to use specific version of support library, try adding this in your project level build.gradle file:
subprojects {
project.configurations.all {
resolutionStrategy.eachDependency { details ->
if (details.requested.group == 'com.android.support'
&& !details.requested.name.contains('multidex') ) {
details.useVersion "yourSupportLibVersion"
}
}
}
}
If you're using support library version 27.0.0 replace yourSupportLibVersion with 27.0.0
Explanation :
Why're we using subprojects?
In a multi-project gradle build, you have a root project and the subprojects. The root project is where the build starts from. Everything else, either added as a dependency in gradle or external libraries added manually, will be processed as a subproject by gradle.
What's ResolutionStrategy?
Quoting from the docs, ResolutionStrategy defines the strategies around dependency resolution. For example, forcing certain dependency versions, substitutions, conflict resolutions or snapshot timeouts.
Why exclude multidex?
The reason why we've excluded multidex from getting forced to our required version is that,the dependency name for multidex also contains com.android.support, which is com.android.support:multidex:someVersion where someVersion isn't the same as the support lib version, it's independent from it and that's the reason we're excluding it from being forced to the support lib verison. Note : If you've more such dependencies, you should add them in the if condition above.
and after we've insured that the dependency is indeed a support library ,then we're simply telling gradle to use our desired version for it.
The library you mentioned is using v25.3.1 of the appcompat library and hasn't been updated in last 4 months.
To avoid the support library version conflict, I think you should clone/download the library and include it manually as a module dependency in your project so that you can update the version of the appcompat library and use it without any problem.
Clone the git repository into another directory.
In Android Studio choose File → Import Module and choose the module directory (the one containing file build.gradle)
Then right click in your module app → open module settings → dependencies → add module dependency

Android support library incompatiblity

I have a jcenter library ToggleButtons I develop that I import into my app. After switching to support 26.1.0 in my app, I receive this error:
All com.android.support libraries must use the exact same version
specification (mixing versions can lead to runtime crashes). Found
versions 26.1.0, 25.3.1
ToggleButtons:
com.android.support:cardview-v7:25.3.1
Main app:
com.android.support:design:26.1.0
I'm using other libraries such as Glide that reference even earlier versions of the support library (I haven't upgraded to 4 yet), but those don't have an issue. Have I designed the library improperly somehow?
This was always a recommendation, now they're making it generate errors.
You absolutely can't run an app with both versions, because that would cause duplicated classes errors. That means you must pick one of those manually now, while previously gradle would automatically choose one for you.
I'd suggest you use the higher number, since doing the opposite risks missing new features/assets that either library or app really depends on.
You can add this between your android and dependencies blocks in your application / library module's build.gradle for each conflict you must manually solve:
def supportLibraryVersion = '26.0.1'
configurations.all {
resolutionStrategy {
force "com.android.support:cardview-v7:$supportLibraryVersion"
}
}
I guess you get the idea of how it works.
Edit:
As noted by #eugen-pechanec the best practice is having all your support libraries with same version throughout all your projects modules. Also, it's best to use the same numbers on build tools (in module's build.gradle, inside android block).
Here's what your app depends on:
+ design:26.1.0
+ appcompat-v7:26.1.0
+ support-v4:26.1.0
+ recyclerview-v7:26.1.0
+ support-v4:26.1.0
Here's what the library depends on:
+ cardview-v7:25.3.1 (i.e. at least 25.3.1)
Here's what it means:
Card view library doesn't have any (runtime) dependency on other support libraries so technically in this case it's safe to use different versions. However this may change at any time.
More importantly your own code does not define cardview-v7 as a dependency so there's no way for gradle to know it should pull updated version as well.
The easiest fix then is just defining the dependency in your build.gradle:
def supportLibraryVersion = '26.0.1'
compile "com.android.support:cardview-v7:$supportLibraryVersion"
No force, nothing special. Upgrading a dependency is not a problem. Only downgrading is.
I still don't understand why Glide doesn't throw this error when they're using support 25.
As hinted above, Glide uses at least support-v4 25.x.x. And because a newer version of support-v4 is already requested by your own module, the dependency gets silently upgraded.

Categories

Resources