Erhannis
Erhannis

Reputation: 4462

Android Studio Gradle not using transitive dependencies

Having experienced this problem in a separate project, I made a test project to verify the issue.

  1. Opening Android Studio 3.0, I created a new basic project. Its main module was called app.
  2. I added a library module, called libraryone.
  3. In libraryone, I added a dependency to Gson, and added a single class with a single static method using Gson.
  4. In app, I added a dependency to libraryone. I tested it plain, as well as with transitive = true, as the internet seemed to vaguely concur that doing so might help. (It did not.)
  5. In app, in MainActivity, I used the class from libraryone. This worked (as long as I didn't have transitive = false set, instead).
  6. However, I cannot reference Gson from MainActivity itself. It gives a "Cannot resolve symbol 'Gson'" error, both from Android Studio, as well as from the command line Gradle. This is...unusual and aggravating, as it means you have to repeat common dependencies all throughout a group of projects, and the classes are included in the output anyway. Surely either I'm doing something wrong, or this is a bug?

My code is as follows:

MainActivity.java (in app)

package com.erhannis.test.dependencytest;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import com.erhannis.test.libraryone.LibClass;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        String s = LibClass.convert(new Object());
        System.out.println("out: " + s);

        new com.google.gson.Gson(); // This line gives a compile-time error: "Cannot resolve symbol 'Gson'"
    }
}

LibClass.java (in libraryone)

package com.erhannis.test.libraryone;

import com.google.gson.Gson;

public class LibClass {
    public static String convert(Object o) {
        Gson gson = new Gson();
        return gson.toJson(o);
    }
}

build.gradle (app)

apply plugin: 'com.android.application'

android {
    compileSdkVersion 26
    defaultConfig {
        applicationId "com.erhannis.test.dependencytest"
        minSdkVersion 18
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    implementation 'com.android.support:appcompat-v7:26.1.0'
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'

    implementation (project(path: ':libraryone'))
}

build.gradle (libraryone)

apply plugin: 'com.android.library'

android {
    compileSdkVersion 26
    defaultConfig {
        minSdkVersion 18
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    implementation 'com.android.support:appcompat-v7:26.1.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'

    implementation 'com.google.code.gson:gson:2.8.2'
}

Upvotes: 9

Views: 6957

Answers (3)

Kasun Thilina
Kasun Thilina

Reputation: 1681

The best solution for this is to create a private Github/Gitlab repository for the aar dependency. You can also make it public if you want. The aar file does not contain transitive dependencies when used locally. We need to publish it somewhere with the .pom file. Here's how to do it,

  1. create a personal access token in Github

Github -> Settings -> Developer Settings -> Personal Access token - with write:packages / read:packages access level
(create two tokens if you need to distribute privately

  1. Add id 'maven-publish' to plugins in module level gradle

  2. Add the below code to module level gradle

    task sourceJar(type: Jar) {
        from android.sourceSets.main.java.srcDirs
        archiveClassifier.set("sources")
    }
    
     publishing {
         publications {
             bar(MavenPublication) {
                 groupId 'com.your'
                 artifactId 'artifact'
                 version '1.0'
                 artifact sourceJar
                 artifact("$buildDir/outputs/aar/YourLibraryName-release.aar")
                 pom.withXml {
                     final dependenciesNode = asNode().appendNode('dependencies')
                     ext.addDependency = { Dependency dep, String scope ->
                         if (dep.group == null || dep.version == null || dep.name == null || dep.name == "unspecified")
                             return
                         final dependencyNode = dependenciesNode.appendNode('dependency')
                         dependencyNode.appendNode('groupId', dep.group)
                         dependencyNode.appendNode('artifactId', dep.name)
                         dependencyNode.appendNode('version', dep.version)
                         dependencyNode.appendNode('scope', scope)
                         if (!dep.transitive) {
                             final exclusionNode = dependencyNode.appendNode('exclusions').appendNode('exclusion')
                             exclusionNode.appendNode('groupId', '*')
                             exclusionNode.appendNode('artifactId', '*')
                         } else if (!dep.properties.excludeRules.empty) {
                             final exclusionNode = dependencyNode.appendNode('exclusions').appendNode('exclusion')
                             dep.properties.excludeRules.each { ExcludeRule rule ->
                                 exclusionNode.appendNode('groupId', rule.group ?: '*')
                                 exclusionNode.appendNode('artifactId', rule.module ?: '*')
                             }
                         }
                     }
                     configurations.compile.getDependencies().each { dep -> addDependency(dep, "compile") }
                     configurations.api.getDependencies().each { dep -> addDependency(dep, "compile") }
                     configurations.implementation.getDependencies().each { dep -> addDependency(dep, "runtime") }
                 }
             }
         }
    
         repositories {
             maven {
                 name = "GitHubPackages"
                 url = uri("https://maven.pkg.github.com/GITHUB_USERID/REPOSITORY")
                 credentials {
                     username = "GITHUB_USERID" // Better if you use env variable
                     password = "WRITE_ACCESS_TOKEN"
                 }
             }
         }
     }
    
  3. In the Gradle toolbar you can find publish under the publishing subdirectory in your library module directory. ( Make sure to build the aar from build->build in module directory before publish)

  4. To use it in your project,
    add following to your app level gradle file inside android{ }tag

     repositories {
         maven {
             name = "GitHubPackages"
             url = uri("https://maven.pkg.github.com/GITHUB_USERID/REPOSITORY")
             credentials {
                 username = "GITHUB_USERID" // Better if you use env variable 
                 password = "READ_ACCESS_TOKEN"
             }
         }
     }
    
  5. Finally in dependencies

     implementation 'com.your:artifact:1.0'
    

*Gitlab is also pretty similar to this

Upvotes: 0

Nongthonbam Tonthoi
Nongthonbam Tonthoi

Reputation: 12953

It is because you declared the transitive dependency as implementation. Use api instead of implementation change:

implementation 'com.google.code.gson:gson:2.8.2'

to:

api 'com.google.code.gson:gson:2.8.2'

The reason why you should declare the transitive dependency in your library can be found here

Upvotes: 20

Gabriele Mariotti
Gabriele Mariotti

Reputation: 363825

You are using:

implementation (project(path: ':libraryone'))
implementation 'com.google.code.gson:gson:2.8.2'

Check the official doc:

When your module configures an implementation dependency, it's letting Gradle know that the module does not want to leak the dependency to other modules at compile time. That is, the dependency is available to other modules only at runtime.

Here you can find a very good blog about it.

Upvotes: 7

Related Questions