Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Gradle Summit 2017 - Gradle Android recipes exp...

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on.

Gradle Summit 2017 - Gradle Android recipes explained

Android developers often copy & paste Gradle snippets to their projects without full understanding of how they work. This presentation will explain the basics of Groovy and Gradle, and analyze some of the popular snippets, explaining what's going on in them and how we can improve on them.

Avatar for Łukasz Wasylkowski

Łukasz Wasylkowski

June 22, 2017

More Decks by Łukasz Wasylkowski

Other Decks in Programming

Transcript

  1. Groovy syntax def printByClosure(String param, Closure closure) { closure(param) }

    printByClosure("param", { println it }) printByClosure("param") { println it }
  2. Groovy syntax def printByClosure(String param, Closure closure) { closure(param) }

    printByClosure("param", { println it }) printByClosure("param") { println it } printByClosure "param", { println it }
  3. Groovy syntax buildscript({ ext.kotlinVersion = "1.1.2-4" repositories({ jcenter() maven({ url('https:!"maven.fabric.io/public')

    } google() }) … }) buildscript { ext.kotlinVersion = "1.1.2-4" repositories { jcenter() maven { url 'https:!"maven.fabric.io/public' } google() } … }
  4. Groovy syntax buildscript { ext.kotlinVersion = "1.1.2-4" repositories { jcenter()

    maven { url 'https:!"maven.fabric.io/public' } google() } … }
  5. Groovy syntax buildscript { ext.kotlinVersion = "1.1.2-4" repositories { jcenter()

    maven { url 'https:!"maven.fabric.io/public' } google() } … } buildscript
  6. Groovy syntax buildscript { ext.kotlinVersion = "1.1.2-4" repositories { jcenter()

    maven { url 'https:!"maven.fabric.io/public' } google() } … } { ext.kotlinVersion = "1.1.2-4" repositories { jcenter() maven { url 'https:!"maven.fabric.io/public' } google() } … }
  7. Groovy syntax buildscript { ext.kotlinVersion = "1.1.2-4" repositories { jcenter()

    maven { url 'https:!"maven.fabric.io/public' } google() } … } ext.kotlinVersion = "1.1.2-4"
  8. Groovy syntax buildscript { ext.kotlinVersion = "1.1.2-4" repositories { jcenter()

    maven { url 'https:!"maven.fabric.io/public' } google() } … } repositories
  9. Groovy syntax buildscript { ext.kotlinVersion = "1.1.2-4" repositories { jcenter()

    maven { url 'https:!"maven.fabric.io/public' } google() } … } { jcenter() maven { url 'https:!"maven.fabric.io/public' } google() }
  10. Groovy syntax buildscript { ext.kotlinVersion = "1.1.2-4" repositories { jcenter()

    maven { url 'https:!"maven.fabric.io/public' } google() } … } jcenter()
  11. Groovy syntax buildscript { ext.kotlinVersion = "1.1.2-4" repositories { jcenter()

    maven { url 'https:!"maven.fabric.io/public' } google() } … } maven
  12. Groovy syntax buildscript { ext.kotlinVersion = "1.1.2-4" repositories { jcenter()

    maven { url 'https:!"maven.fabric.io/public' } google() } … } { url 'https:!"maven.fabric.io/public' }
  13. Groovy syntax buildscript { ext.kotlinVersion = "1.1.2-4" repositories { jcenter()

    maven { url 'https:!"maven.fabric.io/public' } google() } … } url ‘https:!"maven.fabric.io/public'
  14. Groovy syntax buildscript { ext.kotlinVersion = "1.1.2-4" repositories { jcenter()

    maven { url 'https:!"maven.fabric.io/public' } google() } … } google()
  15. Groovy syntax methodWithNamedParams name: "John", age: 25 def methodWithNamedParams(Map<String, Object>

    params) { println params["name"] println "Age: ${params["age"]}" }
  16. Closures and delegates class WriterOne { def printText(str) { println

    "Printed in One: $str" } } class WriterTwo { def printText(str) { println "Printed in Two: $str" } }
  17. Closures and delegates class WriterOne { def printText(str) { println

    "Printed in One: $str" } } class WriterTwo { def printText(str) { println "Printed in Two: $str" } } def printClosure = { printText "I come from a closure" }
  18. Closures and delegates class WriterOne { def printText(str) { println

    "Printed in One: $str" } } class WriterTwo { def printText(str) { println "Printed in Two: $str" } } def printClosure = { printText "I come from a closure" } printClosure.delegate = new WriterOne() printClosure() !" will print "Printed in One: I come from a closure
  19. Closures and delegates class WriterOne { def printText(str) { println

    "Printed in One: $str" } } class WriterTwo { def printText(str) { println "Printed in Two: $str" } } def printClosure = { printText "I come from a closure" } printClosure.delegate = new WriterOne() printClosure() !" will print "Printed in One: I come from a closure printClosure.delegate = new WriterTwo() printClosure() !" will print "Printed in Two: I come from a closure
  20. (MyProject) . ├── settings.gradle ├── build.gradle ├── gradle │ └──

    wrapper ├── app │ ├── build.gradle │ └── src ├── data │ ├── build.gradle │ └── src └── domain ├── build.gradle └── src Gradle
  21. (MyProject) . ├── settings.gradle ├── build.gradle ├── gradle │ └──

    wrapper ├── app │ ├── build.gradle │ └── src ├── data │ ├── build.gradle │ └── src └── domain ├── build.gradle └── src Project(name: MyProject, …) Project(name: app, …) Project(name: data, …) Project(name: domain, …) Gradle
  22. (MyProject) . ├── settings.gradle ├── build.gradle ├── gradle │ └──

    wrapper ├── app │ ├── build.gradle │ └── src ├── data │ ├── build.gradle │ └── src └── domain ├── build.gradle └── src Project(name: MyProject, …) Project(name: app, …) Project(name: data, …) Project(name: domain, …) Gradle Project(name: MyProject, …)
  23. build.gradle buildscript { ext.kotlinVersion = "1.1.2-4" repositories { jcenter() maven

    { url 'https:!"maven.fabric.io/public' } google() } … }
  24. build.gradle Project buildscript { ext.kotlinVersion = "1.1.2-4" repositories { jcenter()

    maven { url 'https:!"maven.fabric.io/public' } google() } … } buildscript buildscript(@DelegatesTo(ScriptHandler.class) Closure closure)
  25. build.gradle ScriptHandler Project buildscript { ext.kotlinVersion = "1.1.2-4" repositories {

    jcenter() maven { url 'https:!"maven.fabric.io/public' } google() } … } ext.kotlinVersion = "1.1.2-4" repositories { jcenter() maven { url 'https:!"maven.fabric.io/public' } google() repositories(@DelegatesTo(RepositoryHandler.class) Closure closure)
  26. build.gradle ScriptHandler RepositoryHandler Project buildscript { ext.kotlinVersion = "1.1.2-4" repositories

    { jcenter() maven { url 'https:!"maven.fabric.io/public' } google() } … } jcenter() maven { url 'https:!"maven.fabric.io/public' } google() maven(@DelegatesTo(MavenArtifactRepository.class) Closure closure)
  27. APK naming android { applicationVariants.all { variant !# variant.outputs.each {

    it.outputFile = new File( it.outputFile.parentFile, it.outputFile.name.replace(".apk", "-${variant.versionName}.apk")) } } }
  28. *DomainObject collections T T T T T T NamedDomainObjectCollection<T> Live

    notifications and filtering name4: name2: name5: name3: name1: name6:
  29. *DomainObject collections NamedDomainObjectContainer<T> Live notifications and filtering T name6: T

    name5: T name4: T name3: T name2: T name1: create() maybeCreate() configure()
  30. class Person { def name def role @Override String toString()

    { return "$name !$ $role" } def role(newRole) { this.role = newRole } }
  31. NamedDomainObjectCollection<Person> peopleRegistry = project.container(Person.class) class Person { def name def

    role @Override String toString() { return "$name !$ $role" } def role(newRole) { this.role = newRole } }
  32. NamedDomainObjectCollection<Person> peopleRegistry = project.container(Person.class) peopleRegistry.add(new Person(name: "Amy", role: “Tester")) peopleRegistry.add(new

    Person(name: "John", role: "Engineer")) class Person { def name def role @Override String toString() { return "$name !$ $role" } def role(newRole) { this.role = newRole } }
  33. println "Creating the registry” NamedDomainObjectCollection<Person> peopleRegistry = project.container(Person.class) println "Adding

    people” peopleRegistry.add(new Person(name: "Amy", role: “Tester")) peopleRegistry.add(new Person(name: "John", role: "Engineer")) println "Registering the listener” peopleRegistry.each { person !# println person }
  34. println "Creating the registry” NamedDomainObjectCollection<Person> peopleRegistry = project.container(Person.class) println "Adding

    people” peopleRegistry.add(new Person(name: "Amy", role: “Tester")) peopleRegistry.add(new Person(name: "John", role: "Engineer")) println "Registering the listener” peopleRegistry.each { person !# println person } ▶ ./gradlew Creating the registry Registering the listener Adding people
  35. println "Creating the registry” NamedDomainObjectCollection<Person> peopleRegistry = project.container(Person.class) println "Adding

    people” peopleRegistry.add(new Person(name: "Amy", role: “Tester")) peopleRegistry.add(new Person(name: "John", role: "Engineer")) println "Registering the listener” peopleRegistry.all { person !# println person } ▶ ./gradlew Creating the registry Registering the listener Adding people
  36. println "Creating the registry” NamedDomainObjectCollection<Person> peopleRegistry = project.container(Person.class) println "Adding

    people” peopleRegistry.add(new Person(name: "Amy", role: “Tester")) peopleRegistry.add(new Person(name: "John", role: "Engineer")) println "Registering the listener” peopleRegistry.all { person !# println person } ▶ ./gradlew Creating the registry Registering the listener Adding people Amy !$ Tester John !$ Engineer
  37. println "Creating the registry" NamedDomainObjectCollection<Person> peopleRegistry = project.container(Person.class) peopleRegistry.add(new Person(name:

    "Amy", role: “Tester")) println "Registering the listener" peopleRegistry.whenObjectAdded { person !# println person } peopleRegistry.add(new Person(name: "John", role: "Engineer"))
  38. println "Creating the registry" NamedDomainObjectCollection<Person> peopleRegistry = project.container(Person.class) peopleRegistry.add(new Person(name:

    "Amy", role: “Tester")) println "Registering the listener" peopleRegistry.whenObjectAdded { person !# println person } peopleRegistry.add(new Person(name: "John", role: "Engineer")) ▶ ./gradlew Creating the registry Registering the listener John !$ Engineer
  39. def peopleRegistry = project.container(Person.class, { new Person(name: it) }) peopleRegistry.configure

    { Amy { role "Tester" } John { role "Engineer" } } println peopleRegistry.Amy println peopleRegistry.John
  40. android { buildTypes { release { … } } }

    public void buildTypes(Action<NamedDomainObjectContainer<BuildType!& action) { this.checkWritability(); action.execute(this.buildTypes); } AppExtension:
  41. APK naming android { applicationVariants.all { variant !# variant.outputs.each {

    it.outputFile = new File( it.outputFile.parentFile, it.outputFile.name.replace(".apk", "-${variant.versionName}.apk")) } } }
  42. APK naming public DomainObjectSet<ApplicationVariant> AppExtension#getApplicationVariants() android { applicationVariants.all { variant

    !# variant.outputs.each { it.outputFile = new File( it.outputFile.parentFile, it.outputFile.name.replace(".apk", "-${variant.versionName}.apk")) } } } applicationVariants.all
  43. APK naming android { applicationVariants.all { variant !# variant.outputs.each {

    it.outputFile = new File( it.outputFile.parentFile, it.outputFile.name.replace(".apk", "-${variant.versionName}.apk")) } } } variant.outputs.each { public List<BaseVariantOutput> ApplicationVariant#getOutputs()
  44. APK naming android { applicationVariants.all { variant !# variant.outputs.each {

    it.outputFile = new File( it.outputFile.parentFile, it.outputFile.name.replace(".apk", "-${variant.versionName}.apk")) } } } it.outputFile = new File( it.outputFile.parentFile, it.outputFile.name.replace(".apk", "-${variant.versionName}.apk")) public File BaseVariantOutput#getOutputFile() public File BaseVariantOutput#setOutputFile(File file)
  45. Common flavors and build types def androidProjects = [project("app"), project("data"),

    project("mvp")] def androidBuildTypes = ["alpha", "debug", "release"] def androidFlavors = ["staging", “production", “mock”]
  46. Common flavors and build types def androidProjects = [project("app"), project("data"),

    project("mvp")] androidProjects.each { project !# androidBuildTypes.each { project.android.buildTypes.maybeCreate it } androidFlavors.each { project.android.productFlavors.maybeCreate it } } def androidBuildTypes = ["alpha", "debug", "release"] def androidFlavors = ["staging", “production", “mock”]
  47. BuildConfig fields android { buildTypes { release { buildConfigField "boolean",

    "LOG_HTTP_REQUESTS", "false" buildConfigField "boolean", "REPORT_CRASHES", "true" buildConfigField "boolean", "DEBUG_IMAGES", "false" } debug { buildConfigField "boolean", "LOG_HTTP_REQUESTS", "true" buildConfigField "boolean", "REPORT_CRASHES", "false" buildConfigField "boolean", "DEBUG_IMAGES", "true" } !!' } }
  48. android { buildTypes { def BOOLEAN = "boolean" def TRUE

    = "true" def FALSE = "false" def LOG_HTTP_REQUESTS = "LOG_HTTP_REQUESTS" def REPORT_CRASHES = "REPORT_CRASHES" def DEBUG_IMAGES = "DEBUG_IMAGES" release { buildConfigField BOOLEAN, LOG_HTTP_REQUESTS, FALSE buildConfigField BOOLEAN, REPORT_CRASHES, TRUE buildConfigField BOOLEAN, DEBUG_IMAGES, FALSE } debug { buildConfigField BOOLEAN, LOG_HTTP_REQUESTS, TRUE buildConfigField BOOLEAN, REPORT_CRASHES, FALSE buildConfigField BOOLEAN, DEBUG_IMAGES, TRUE } } }
  49. buildTypes { release { buildConfigField "boolean", "LOG_HTTP_REQUESTS", "false" buildConfigField "boolean",

    "REPORT_CRASHES", "true" buildConfigField "boolean", "DEBUG_IMAGES", "true" } }
  50. void buildTypes(Action<NamedDomainObjectContainer<BuildType!& action) buildTypes { release { buildConfigField "boolean", "LOG_HTTP_REQUESTS",

    "false" buildConfigField "boolean", "REPORT_CRASHES", "true" buildConfigField "boolean", "DEBUG_IMAGES", "true" } } buildTypes { }
  51. buildTypes { release { buildConfigField "boolean", "LOG_HTTP_REQUESTS", "false" buildConfigField "boolean",

    "REPORT_CRASHES", "true" buildConfigField "boolean", "DEBUG_IMAGES", "true" } } BuildType(name: release) { buildConfigField "boolean", "LOG_HTTP_REQUESTS", "false" buildConfigField "boolean", "REPORT_CRASHES", "true" buildConfigField "boolean", "DEBUG_IMAGES", "true" }
  52. def configureBuildType(buildType, isDebug) { buildType.buildConfigField "boolean", "LOG_HTTP_REQUESTS", "${isDebug}" buildType.buildConfigField "boolean",

    "REPORT_CRASHES", "${!isDebug}" buildType.buildConfigField "boolean", "DEBUG_IMAGES", "${isDebug}" }
  53. def configureBuildType(buildType, isDebug) { buildType.buildConfigField "boolean", "LOG_HTTP_REQUESTS", "${isDebug}" buildType.buildConfigField "boolean",

    "REPORT_CRASHES", "${!isDebug}" buildType.buildConfigField "boolean", "DEBUG_IMAGES", "${isDebug}" } configureBuildType(android.buildTypes.release, false)
  54. def configureBuildType(buildType, isDebug) { buildType.buildConfigField "boolean", "LOG_HTTP_REQUESTS", "${isDebug}" buildType.buildConfigField "boolean",

    "REPORT_CRASHES", "${!isDebug}" buildType.buildConfigField "boolean", "DEBUG_IMAGES", "${isDebug}" } configureBuildType(android.buildTypes.release, false) android { buildTypes { release { configureBuildType(???, false) } debug { configureBuildType(???, true) } } }
  55. def configureBuildType(buildType, isDebug) { buildType.buildConfigField "boolean", "LOG_HTTP_REQUESTS", "${isDebug}" buildType.buildConfigField "boolean",

    "REPORT_CRASHES", "${!isDebug}" buildType.buildConfigField "boolean", "DEBUG_IMAGES", "${isDebug}" } android { buildTypes { release { configureBuildType(delegate, false) } debug { configureBuildType(delegate, true) } } } configureBuildType(android.buildTypes.release, false)
  56. android { buildTypes { release { configureBuildType(delegate, false) } }

    } android { buildTypes { release { configureBuildType(delegate, false) } debug { configureBuildType(delegate, true) } alpha { configureBuildType(delegate, false) } mock { configureBuildType(delegate, true) }
  57. Dependency resolution androidTestCompile (‘com.android.support.test.espresso:espresso-contrib:2.2') { exclude group: 'com.android.support', module: 'appcompat-v7'

    exclude group: 'com.android.support', module: 'support-v4' exclude module: 'recyclerview-v7' } compile('com.crashlytics.sdk.android:crashlytics:2.6.7@aar') { transitive = true }
  58. ▶ ./gradlew app:dependencies !)configuration compile dependencies { compile 'com.jakewharton.rxbinding2:rxbinding:2.0.0' }

    compile - Dependencies for source set 'main' +!!* com.jakewharton.rxbinding2:rxbinding:2.0.0 | +!!* io.reactivex.rxjava2:rxjava:2.0.2 (*) | +!!* io.reactivex.rxjava2:rxandroid:2.0.0 | | \!!* io.reactivex.rxjava2:rxjava:2.0.0 !# 2.0.2 (*) | \!!* com.android.support:support-annotations:25.1.1 !# 25.3.1
  59. App module RxBindings 2.0.0 RxJava 2.0.2 RxAndroid 2.0.0 compile 'com.jakewharton.rxbinding2:rxbinding:2.0.0'

    +!!* com.jakewharton.rxbinding2:rxbinding:2.0.0 | +!!* io.reactivex.rxjava2:rxjava:2.0.2 (*) | +!!* io.reactivex.rxjava2:rxandroid:2.0.0
  60. App module RxBindings 2.0.0 RxJava 2.0.2 RxAndroid 2.0.0 RxJava 2.0.0

    compile 'com.jakewharton.rxbinding2:rxbinding:2.0.0' +!!* com.jakewharton.rxbinding2:rxbinding:2.0.0 | +!!* io.reactivex.rxjava2:rxjava:2.0.2 (*) | +!!* io.reactivex.rxjava2:rxandroid:2.0.0 | | \!!* io.reactivex.rxjava2:rxjava:2.0.0 !# 2.0.2 (*)
  61. App module RxBindings 2.0.0 RxJava 2.0.2 RxAndroid 2.0.0 RxJava 2.0.0

    +!!* com.jakewharton.rxbinding2:rxbinding:2.0.0 | +!!* io.reactivex.rxjava2:rxjava:2.0.2 (*) | +!!* io.reactivex.rxjava2:rxandroid:2.0.0 | | \!!* io.reactivex.rxjava2:rxjava:2.0.0 !# 2.0.2 (*) 2.0.0 !# 2.0.2 (*)
  62. App module RxBindings 2.0.0 RxJava 2.0.2 RxAndroid 2.0.0 RxJava 2.0.0

    +!!* com.jakewharton.rxbinding2:rxbinding:2.0.0 | +!!* io.reactivex.rxjava2:rxjava:2.0.2 (*) | +!!* io.reactivex.rxjava2:rxandroid:2.0.0 | | \!!* io.reactivex.rxjava2:rxjava:2.0.0 !# 2.0.2 (*) 2.0.0 !# 2.0.2 (*)
  63. App module RxBindings 2.0.0 RxJava 2.0.2 RxAndroid 2.0.0 RxJava 2.0.0

    +!!* com.jakewharton.rxbinding2:rxbinding:2.0.0 | +!!* io.reactivex.rxjava2:rxjava:2.0.2 (*) | +!!* io.reactivex.rxjava2:rxandroid:2.0.0 | | \!!* io.reactivex.rxjava2:rxjava:2.0.0 !# 2.0.2 (*) 2.0.0 !# 2.0.2 (*) RxJava 2.0.2 RxJava 2.0.0
  64. ▶ ./gradlew app:dependencies !)configuration compile dependencies { compile(‘com.jakewharton.rxbinding2:rxbinding:2.0.0’) { transitive

    = false } } compile - Dependencies for source set 'main' +!!* com.jakewharton.rxbinding2:rxbinding:2.0.0
  65. dependencies { compile(‘com.jakewharton.rxbinding2:rxbinding:2.0.0’) { transitive = false } } Configuration

    Configuration.configure(Closure) configurationName "group:name:version:classifier@extension" configurationName("group:name:version:classifier@extension") { }
  66. ▶ gw app:dependencies !)configuration androidTestCompile androidTestCompile - Classpath for compiling

    the androidTest sources. \!!* com.android.support.test.espresso:espresso-contrib:2.2 +!!* com.google.android.apps.common.testing.accessibility.framework:accessibility-test-framework:2.0 | \!!* org.hamcrest:hamcrest-core:1.3 +!!* com.android.support:support-v4:22.2.0 | \!!* com.android.support:support-annotations:22.2.0 +!!* com.android.support:recyclerview-v7:22.2.0 | +!!* com.android.support:support-annotations:22.2.0 | \!!* com.android.support:support-v4:22.2.0 (*) \!!* com.android.support.test.espresso:espresso-core:2.2 +!!* com.android.support.test.espresso:espresso-idling-resource:2.2 +!!* com.squareup:javawriter:2.1.1 +!!* javax.inject:javax.inject:1 +!!* org.hamcrest:hamcrest-library:1.3 | \!!* org.hamcrest:hamcrest-core:1.3 +!!* com.android.support.test:rules:0.3 | \!!* com.android.support.test:runner:0.3 | +!!* com.android.support.test:exposed-instrumentation-api-publish:0.3 | +!!* junit:junit:4.12 | | \!!* org.hamcrest:hamcrest-core:1.3 | \!!* com.android.support:support-annotations:22.2.0 +!!* org.hamcrest:hamcrest-integration:1.3 | \!!* org.hamcrest:hamcrest-library:1.3 (*) +!!* com.google.code.findbugs:jsr305:2.0.1 +!!* javax.annotation:javax.annotation-api:1.2 \!!* com.android.support.test:runner:0.3 (*) androidTestCompile - Classpath for compiling the androidTest sources. \!!* com.android.support.test.espresso:espresso-contrib:2.2 +!!* com.android.support:recyclerview-v7:22.2.0 | +!!* com.android.support:support-annotations:22.2.0 | \!!* com.android.support:support-v4:22.2.0 (*) dependencies { androidTestCompile 'com.android.support.test.espresso:espresso-contrib:2.2' }
  67. ▶ gw app:dependencies !)configuration androidTestCompile androidTestCompile - Classpath for compiling

    the androidTest sources. \!!* com.android.support.test.espresso:espresso-contrib:2.2 +!!* com.google.android.apps.common.testing.accessibility.framework:accessibility-test-framework:2.0 | \!!* org.hamcrest:hamcrest-core:1.3 +!!* com.android.support:support-v4:22.2.0 | \!!* com.android.support:support-annotations:22.2.0 +!!* com.android.support:recyclerview-v7:22.2.0 | +!!* com.android.support:support-annotations:22.2.0 | \!!* com.android.support:support-v4:22.2.0 (*) \!!* com.android.support.test.espresso:espresso-core:2.2 +!!* com.android.support.test.espresso:espresso-idling-resource:2.2 +!!* com.squareup:javawriter:2.1.1 +!!* javax.inject:javax.inject:1 +!!* org.hamcrest:hamcrest-library:1.3 | \!!* org.hamcrest:hamcrest-core:1.3 +!!* com.android.support.test:rules:0.3 | \!!* com.android.support.test:runner:0.3 | +!!* com.android.support.test:exposed-instrumentation-api-publish:0.3 | +!!* junit:junit:4.12 | | \!!* org.hamcrest:hamcrest-core:1.3 | \!!* com.android.support:support-annotations:22.2.0 +!!* org.hamcrest:hamcrest-integration:1.3 | \!!* org.hamcrest:hamcrest-library:1.3 (*) +!!* com.google.code.findbugs:jsr305:2.0.1 +!!* javax.annotation:javax.annotation-api:1.2 \!!* com.android.support.test:runner:0.3 (*) androidTestCompile - Classpath for compiling the androidTest sources. \!!* com.android.support.test.espresso:espresso-contrib:2.2 +!!* com.android.support:recyclerview-v7:22.2.0 | +!!* com.android.support:support-annotations:22.2.0 | \!!* com.android.support:support-v4:22.2.0 (*) dependencies { androidTestCompile 'com.android.support.test.espresso:espresso-contrib:2.2' }
  68. ▶ gw app:dependencies !)configuration androidTestCompile androidTestCompile - Classpath for compiling

    the androidTest sources. \!!* com.android.support.test.espresso:espresso-contrib:2.2 +!!* com.google.android.apps.common.testing.accessibility.framework:accessibility-test-framework:2.0 | \!!* org.hamcrest:hamcrest-core:1.3 +!!* com.android.support:support-v4:22.2.0 | \!!* com.android.support:support-annotations:22.2.0 +!!* com.android.support:recyclerview-v7:22.2.0 | +!!* com.android.support:support-annotations:22.2.0 | \!!* com.android.support:support-v4:22.2.0 (*) \!!* com.android.support.test.espresso:espresso-core:2.2 +!!* com.android.support.test.espresso:espresso-idling-resource:2.2 +!!* com.squareup:javawriter:2.1.1 +!!* javax.inject:javax.inject:1 +!!* org.hamcrest:hamcrest-library:1.3 | \!!* org.hamcrest:hamcrest-core:1.3 +!!* com.android.support.test:rules:0.3 | \!!* com.android.support.test:runner:0.3 | +!!* com.android.support.test:exposed-instrumentation-api-publish:0.3 | +!!* junit:junit:4.12 | | \!!* org.hamcrest:hamcrest-core:1.3 | \!!* com.android.support:support-annotations:22.2.0 +!!* org.hamcrest:hamcrest-integration:1.3 | \!!* org.hamcrest:hamcrest-library:1.3 (*) +!!* com.google.code.findbugs:jsr305:2.0.1 +!!* javax.annotation:javax.annotation-api:1.2 \!!* com.android.support.test:runner:0.3 (*) androidTestCompile - Classpath for compiling the androidTest sources. \!!* com.android.support.test.espresso:espresso-contrib:2.2 +!!* com.android.support:recyclerview-v7:22.2.0 | +!!* com.android.support:support-annotations:22.2.0 | \!!* com.android.support:support-v4:22.2.0 (*) dependencies { androidTestCompile 'com.android.support.test.espresso:espresso-contrib:2.2' compile ‘com.android.support:recyclerview-v7:25.3.1' }
  69. dependencies { androidTestCompile 'com.android.support.test.espresso:espresso-contrib:2.2' compile ‘com.android.support:recyclerview-v7:25.3.1' } Conflict with dependency

    'com.android.support:recyclerview-v7' in project ‘:app'. Resolved versions for app (25.3.1) and test app (22.2.0) differ. See http:!"g.co/androidstudio/app-test-app-conflict for details.
  70. Exclude conflicting dependencies androidTestCompile ('com.android.support.test.espresso:espresso-contrib:2.2'){ exclude group: 'com.android.support' } androidTestCompile

    ('com.android.support.test.espresso:espresso-contrib:2.2'){ exclude group: 'com.android.support', module: 'appcompat-v7' exclude group: 'com.android.support', module: 'support-v4' exclude module: 'recyclerview-v7' }
  71. Exclude conflicting dependencies androidTestCompile ('com.android.support.test.espresso:espresso-contrib:2.2'){ exclude group: 'com.android.support' } androidTestCompile

    ('com.android.support.test.espresso:espresso-contrib:2.2'){ exclude group: 'com.android.support', module: 'appcompat-v7' exclude group: 'com.android.support', module: 'support-v4' exclude module: 'recyclerview-v7' } androidTestCompile - Classpath for compiling the androidTest sources. \!!* com.android.support.test.espresso:espresso-contrib:2.2 +!!* com.google.android.apps.common.testing.accessibility.framework:accessibility-test-framework:2.0 | \!!* org.hamcrest:hamcrest-core:1.3 \!!* com.android.support.test.espresso:espresso-core:2.2 +!!* com.android.support.test.espresso:espresso-idling-resource:2.2 +!!* com.squareup:javawriter:2.1.1 +!!* javax.inject:javax.inject:1 +!!* org.hamcrest:hamcrest-library:1.3 | \!!* org.hamcrest:hamcrest-core:1.3 +!!* com.android.support.test:rules:0.3 | \!!* com.android.support.test:runner:0.3 | +!!* com.android.support.test:exposed-instrumentation-api-publish:0.3 | +!!* junit:junit:4.12 | | \!!* org.hamcrest:hamcrest-core:1.3 | \!!* com.android.support:support-annotations:22.2.0 +!!* org.hamcrest:hamcrest-integration:1.3 | \!!* org.hamcrest:hamcrest-library:1.3 (*) +!!* com.google.code.findbugs:jsr305:2.0.1 +!!* javax.annotation:javax.annotation-api:1.2 \!!* com.android.support.test:runner:0.3 (*)
  72. Exclude conflicting dependencies androidTestCompile ('com.android.support.test.espresso:espresso-contrib:2.2'){ exclude group: 'com.android.support' } androidTestCompile

    - Classpath for compiling the androidTest sources. \!!* com.android.support.test.espresso:espresso-contrib:2.2 +!!* com.google.android.apps.common.testing.accessibility.framework:accessibility-test-framework:2.0 | \!!* org.hamcrest:hamcrest-core:1.3 \!!* com.android.support.test.espresso:espresso-core:2.2 +!!* com.android.support.test.espresso:espresso-idling-resource:2.2 +!!* com.squareup:javawriter:2.1.1 +!!* javax.inject:javax.inject:1 +!!* org.hamcrest:hamcrest-library:1.3 | \!!* org.hamcrest:hamcrest-core:1.3 +!!* com.android.support.test:rules:0.3 | \!!* com.android.support.test:runner:0.3 | +!!* com.android.support.test:exposed-instrumentation-api-publish:0.3 | +!!* junit:junit:4.12 | | \!!* org.hamcrest:hamcrest-core:1.3 | \!!* com.android.support:support-annotations:22.2.0 +!!* org.hamcrest:hamcrest-integration:1.3 | \!!* org.hamcrest:hamcrest-library:1.3 (*) +!!* com.google.code.findbugs:jsr305:2.0.1 +!!* javax.annotation:javax.annotation-api:1.2 \!!* com.android.support.test:runner:0.3 (*)
  73. Explicitly request specific version androidTestCompile "com.android.support.test.espresso:espresso-contrib:2.2" androidTestCompile "com.android.support:support-v4:25.3.1" androidTestCompile "com.android.support:recyclerview-v7:25.3.1"

    compile "com.android.support:support-v4:25.3.1" compile “com.android.support:recyclerview-v7:25.3.1" androidTestCompile "com.android.support.test.espresso:espresso-contrib:2.2"