Is it possible to get dependency version at runtime, including from library itself?

Viewed 587

Background

Suppose I make an Android library called "MySdk", and I publish it on Jitpack/Maven.

The user of the SDK would use it by adding just the dependency of :

implementation 'com.github.my-sdk:MySdk:1.0.1'

What I'd like to get is the "1.0.1" part from it, whether I do it from within the Android library itself (can be useful to send to the SDK-server which version is used), or from the app that uses it (can be useful to report about specific issues, including via Crashlytics).

The problem

I can't find any reflection or gradle task to reach it.

What I've tried

Searching about it, if I indeed work on the Android library (that is used as a dependency), all I've found is that I can manage the version myself, via code.

Some said I could use BuildConfig of the package name of the library, but then it means that if I forget to update the code a moment before I publish the dependency, it will use the wrong value. Example of using this method:

plugins {
...
}
final def sdkVersion = "1.0.22"

android {
    ...
    buildTypes {
        release {
            ...
            buildConfigField "String", "SDK_VERSION", "\"" + sdkVersion + "\""
        }
       debug {
            buildConfigField "String", "SDK_VERSION", "\"" + sdkVersion + "-unreleased\""
        }
    }

Usage is just checking the value of BuildConfig.SDK_VERSION (after building).

Another possible solution is perhaps from gradle task inside the Android-library, that would be forced to be launched whenever you build the app that uses this library. However, I've failed to find how do it (found something here)

The question

Is it possible to query the dependency version from within the Android library of the dependency (and from the app that uses it, of course), so that I could use it during runtime?

Something automatic, that won't require me to update it before publishing ?

Maybe using Gradle task that is defined in the library, and forced to be used when building the app that uses the library?

3 Answers

You can use a Gradle task to capture the version of the library as presented in the build.gradle dependencies and store the version information in BuildConfig.java for each build type.

The task below captures the version of the "appcompat" dependency as an example.

dependencies {
    implementation 'androidx.appcompat:appcompat:1.4.0'
}

task CaptureLibraryVersion {
    def libDef = project.configurations.getByName('implementation').allDependencies.matching {
        it.group.equals("androidx.appcompat") && it.name.equals("appcompat")
    }
    if (libDef.size() > 0) {
        android.buildTypes.each {
            it.buildConfigField 'String', 'LIB_VERSION', "\"${libDef[0].version}\""
        }
    }
}

For my example, the "appcompat" version was 1.4.0. After the task is run, BuildConfig.java contains

// Field from build type: debug
public static final String LIB_VERSION = "1.4.0";

You can reference this field in code with BuildConfig.LIB_VERSION. The task can be automatically run during each build cycle.

The simple answer to your question is 'yes' - you can do it. But if you want a simple solution to do it so the answer transforms to 'no' - there is no simple solution.

The libraries are in the classpath of your package, thus the only way to access their info at the runtime would be to record needed information during the compilation time and expose it to your application at the runtime.

There are two major 'correct' ways and you kinda have described them in your question but I will elaborate a bit.

  1. The most correct way and relatively easy way is to expose all those variables as BuildConfig or String res values via gradle pretty much as described here. You can try to generify the approach for this using local-prefs(or helper gradle file) to store versions and use them everywhere it is needed. More info here, here, and here
  2. The second correct, but much more complicated way is to write a gradle plugin or at least some set of tasks for collecting needed values during compile-time and providing an interface(usually via your app assets or res) for your app to access them during runtime. A pretty similar thing is already implemented for google libraries in Google Play services Plugins so it would be a good place to start.

All the other possible implementations are variations of the described two or their combination.

You can create buildSrc folder and manage dependencies in there. after that, you can import & use Versions class in anywhere of your app.

Related