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

Deep-er Dive into the Android Gradle Plugin (Droidcon SF 2018)

Deep-er Dive into the Android Gradle Plugin (Droidcon SF 2018)

Note: Additional content since Droidcon London 2018

Android developers use it every day, but who knows what actually goes on under the hood of the Android Gradle Plugin?

In this talk, you'll learn about AGP internals and answer questions like:

* What's D8 and R8?
* What is mergeResources and why does it take so long?
* Why are there so many tasks?
* How can I measure and speed up my builds?

You'll come away with a better understanding of the toolchain which will make you a better equipped Android developer!

Video: coming soon!

John Rodriguez

November 20, 2018
Tweet

More Decks by John Rodriguez

Other Decks in Technology

Transcript

  1. Deep Dive into the Android Gradle Plugin John Rodriguez

  2. Deeper Dive into the Android Gradle Plugin John Rodriguez

  3. None
  4. None
  5. Build phases

  6. • Initialization • Configuration • Execution Build phases rootProject.name =

    'cash' include ':analytics' include ':app' include ':db' include ':keypad' include ':presenters' include ':protos' include ':screens' include ':viewmodels'
  7. • Initialization • Configuration • Execution Build phases Settings rootProject.name

    = 'cash' include ':analytics' include ':app' include ':db' include ':keypad' include ':presenters' include ':protos' include ':screens' include ':viewmodels' project project project project project project project project project
  8. • Initialization • Configuration • Execution Build phases project('cash') project(':analytics')

    project(':db') project(':app') project(':keypad') project(':presenters') project(':protos') project(':screens') project(':viewmodels')
  9. • Initialization • Configuration • Execution Build phases project('cash') project(':analytics')

    project(':db') project(':app') project(':keypad') project(':presenters') project(':protos') project(':screens') project(':viewmodels') > Configure project : > Configure project :analytics > Configure project :app > Configure project :db > Configure project :keypad > Configure project :presenters > Configure project :protos > Configure project :screens > Configure project :viewmodels
  10. • Initialization • Configuration • Execution Build phases project('cash') project(':analytics')

    project(':db') project(':app') project(':keypad') project(':presenters') project(':protos') project(':screens') project(':viewmodels') > Configure project : > Configure project :db > Configure project :protos ./gradlew :db:assembleDebug —configure-on-demand
  11. Configuration buildscript { repositories { google() jcenter() }d dependencies {

    classpath 'com.android.tools.build:gradle:3.2.1' }f }e
  12. Configuration buildscript { repositories { google() jcenter() }d dependencies {

    classpath 'com.android.tools.build:gradle:3.2.1' classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.2.71' classpath 'net.ltgt.gradle:gradle-errorprone-plugin:0.0.16' classpath 'com.squareup.sqldelight:android-gradle-plugin:1.0.0' }f }e
  13. Configuration apply plugin: 'com.android.application' apply plugin: 'com.android.library'

  14. class GreetingPlugin implements Plugin<Project> { @Override void apply(Project project) {

    }f }e
  15. class GreetingPlugin implements Plugin<Project> { @Override void apply(Project project) {

    project.tasks.register('Greeting') { it.doLast { println 'Hello from the Greeting Plugin' }b }a }f }e
  16. class GreetingPlugin implements Plugin<Project> { @Override void apply(Project project) {

    project.tasks.register('Greeting') { it.doLast { println 'Hello from the Greeting Plugin' }b }a }f }e project.apply plugin: GreetingPlugin
  17. class GreetingPlugin implements Plugin<Project> { @Override void apply(Project project) {

    project.tasks.register('Greeting') { it.doLast { println 'Hello from the Greeting Plugin' }b }a }f }e apply plugin: GreetingPlugin
  18. class GreetingPluginExtension { String message String greeter }a class GreetingPlugin

    implements Plugin<Project> { @Override void apply(Project project) { project.tasks.register('Greeting') { it.doLast { println 'Hello from the Greeting Plugin' }b }a }f }e apply plugin: GreetingPlugin
  19. class GreetingPluginExtension {1 String message String greeter }a class GreetingPlugin

    implements Plugin<Project> {2 @Override void apply(Project project) {3 def extension = project.extensions.create('greeting', GreetingPluginExtension) project.tasks.register('Greeting') {4 it.doLast {0 println 'Hello from the Greeting Plugin' }b }a }f }e apply plugin: GreetingPlugin
  20. class GreetingPluginExtension {1 String message String greeter }a class GreetingPlugin

    implements Plugin<Project> {2 @Override void apply(Project project) {3 def extension = project.extensions.create('greeting', GreetingPluginExtension) project.tasks.register('Greeting') {4 it.doLast {0 println "${extension.message} from ${extension.greeter}" }b }a }f }e apply plugin: GreetingPlugin
  21. apply plugin: GreetingPlugin greeting { message 'Hi' greeter 'Gradle' }

    $ ./gradlew Greeting > Task :Greeting Hi from Gradle
  22. interface Project extends ExtensionAware, PluginAware { } interface ExtensionAware {

    ExtensionContainer getExtensions(); } interface PluginAware { void apply(…); PluginManager getPluginManager(); }
  23. None
  24. None
  25. None
  26. class SomeTask extends DefaultTask { @OutputDirectory File getOutputDir() {…} @InputFiles

    List<File> getInputFiles() {…} @TaskAction void doStuff() {…}
  27. None
  28. None
  29. None
  30. None
  31. None
  32. None
  33. None
  34. • Initialization • Configuration • Execution Build phases

  35. • Initialization • Configuration • Execution Build phases ./gradlew :db:assembleDebug

  36. Notifications project.tasks.whenTaskAdded {} project.beforeEvaluate {} project.afterEvaluate {} gradle.projectsEvaluated {} gradle.taskGraph.whenReady

    {} gradle.taskGraph.beforeTask {} gradle.taskGraph.afterTask {}
  37. allprojectsa{ }a

  38. allprojectsa{ project -> project.tasks.whenTaskAdded { task -> println "added task:

    " + task.name } }a
  39. allprojectsa{ project -> project.tasks.whenTaskAdded { task -> println "added task:

    " + task.name } afterEvaluate { project -> println "just evaluated: $project" } }a
  40. > Configure project :android_library added task: clean added task: assemble

    added task: check added task: build added task: preBuild added task: extractProguardFiles added task: sourceSets added task: compileLint added task: lint … just evaluated: project ':android_library' added task: prepareLintJar added task: createMockableJar added task: connectedAndroidTest added task: deviceAndroidTest added task: test added task: assembleDebug added task: preDebugBuild added task: generateDebugSources added task: generateDebugResources
  41. Variants

  42. buildTypes { debug { applicationIdSuffix '.debug' minifyEnabled false proguardFile file('proguard-cash.pro')

    proguardFile file('proguard-cash-debug.pro') } release { minifyEnabled true shrinkResources true proguardFile file('proguard-cash.pro') } }
  43. productFlavors { flavorDimensions 'environment' internal { dimension 'environment' applicationId 'com.squareup.cash.beta'

    } production { dimension 'environment' applicationId 'com.squareup.cash' } } debug release
  44. production internal release debug

  45. production internal Release release debug internal

  46. production internal release debug internal release Release

  47. production internal release debug internalDebug internalRelease productionDebug productionRelease release

  48. Debugging Builds

  49. $ ./gradlew task

  50. $ ./gradlew -Dorg.gradle.debug=true task

  51. $ ./gradlew --no-daemon --no-build-cache —rerun-tasks / -Dorg.gradle.debug=true task

  52. $ ./gradlew --no-daemon --no-build-cache —rerun-tasks / -Dorg.gradle.debug=true task To honour

    the JVM settings for this build a new JVM will be forked. Please consider using the daemon: https://docs.gradle.org/4.10.2/ userguide/gradle_daemon.html. > Starting Daemon
  53. None
  54. dependencies { compileOnly ‘com.android.tools.build:gradle:3.2.1' }

  55. ?

  56. gradle.taskGraph.whenReady { def dot = new File(rootProject.buildDir, 'project.dot') }c

  57. gradle.taskGraph.whenReady { def dot = new File(rootProject.buildDir, 'project.dot') gradle.taskGraph.allTasks.each {

    task -> task.taskDependencies.getDependencies(task).each { dep -> // add task -> dep to graph }a } }c
  58. gradle.taskGraph.whenReady { def dot = new File(rootProject.buildDir, 'project.dot') gradle.taskGraph.allTasks.each {

    task -> task.taskDependencies.getDependencies(task).each { dep -> // add task -> dep to graph }a }b 'dot -Tpng -O project.dot'.execute(…) }c
  59. None
  60. None
  61. None
  62. None
  63. None
  64. None
  65. None
  66. None
  67. gradle.taskGraph.whenReady { def dot = new File(rootProject.buildDir, 'project.dot') gradle.taskGraph.allTasks.each {

    task -> task.taskDependencies.getDependencies(task).each { dep -> // add task -> dep to graph }a }b 'dot -Tpng -O project.dot'.execute(…) }c
  68. gradle.taskGraph.whenReady { def dot = new File(rootProject.buildDir, 'project.dot') gradle.taskGraph.allTasks.each {

    task -> task.taskDependencies.getDependencies(task).each {…}a }b 'dot -Tpng -O project.dot'.execute(…) }c
  69. gradle.taskGraph.whenReady { def dot = new File(rootProject.buildDir, 'project.dot') def startTasks

    = gradle.startParameter.getTaskNames().join(" ") println "command: ./gradlew " + startTasks gradle.taskGraph.allTasks.each { task -> println "task: " + task.name + ", class: " + task.class.name task.taskDependencies.getDependencies(task).each {…}a }b 'dot -Tpng -O project.dot'.execute(…) }c
  70. command: ./gradlew app:assembleDebug compileJava -> JavaCompile processResources -> ProcessResources classes

    -> DefaultTask jar -> Jar checkDebugClasspath -> AppClasspathCheckTask preBuild -> DefaultTask preDebugBuild -> AppPreBuildTask compileDebugAidl -> AidlCompile compileDebugRenderscript -> RenderscriptCompile checkDebugManifest -> CheckManifest generateDebugBuildConfig -> GenerateBuildConfig prepareLintJar -> PrepareLintJar mainApkListPersistenceDebug -> MainApkListPersistence generateDebugResValues -> GenerateResValues generateDebugResources -> DefaultTask mergeDebugResources -> MergeResources …e
  71. command: ./gradlew app:assembleDebug compileJava -> JavaCompile processResources -> ProcessResources classes

    -> DefaultTask jar -> Jar checkDebugClasspath -> AppClasspathCheckTask preBuild -> DefaultTask preDebugBuild -> AppPreBuildTask compileDebugAidl -> AidlCompile compileDebugRenderscript -> RenderscriptCompile checkDebugManifest -> CheckManifest generateDebugBuildConfig -> GenerateBuildConfig prepareLintJar -> PrepareLintJar mainApkListPersistenceDebug -> MainApkListPersistence generateDebugResValues -> GenerateResValues generateDebugResources -> DefaultTask mergeDebugResources -> MergeResources …e
  72. command: ./gradlew app:assembleDebug compileJava -> JavaCompile processResources -> ProcessResources classes

    -> DefaultTask jar -> Jar checkDebugClasspath -> AppClasspathCheckTask preBuild -> DefaultTask preDebugBuild -> AppPreBuildTask compileDebugAidl -> AidlCompile compileDebugRenderscript -> RenderscriptCompile checkDebugManifest -> CheckManifest generateDebugBuildConfig -> GenerateBuildConfig prepareLintJar -> PrepareLintJar mainApkListPersistenceDebug -> MainApkListPersistence generateDebugResValues -> GenerateResValues generateDebugResources -> DefaultTask mergeDebugResources -> MergeResources …e
  73. gradle.taskGraph.whenReady { def dot = new File(rootProject.buildDir, 'project.dot') def startTasks

    = gradle.startParameter.getTaskNames().join(" ") println "command: ./gradlew " + startTasks gradle.taskGraph.allTasks.each { task -> println "task: " + task.name + ", class: " + task.class.name task.taskDependencies.getDependencies(task).each {…}a }b 'dot -Tpng -O project.dot'.execute(…) }c
  74. gradle.taskGraph.whenReady { def dot = new File(rootProject.buildDir, 'project.dot') def startTasks

    = gradle.startParameter.getTaskNames().join(" ") println "command: ./gradlew " + startTasks gradle.taskGraph.allTasks.each { task -> println "task: " + task.name + ", class: " + task.class.name println " inputs: " task.inputs.each { it.files.each { println " " + it } } println " outputs: " task.outputs.each { it.files.each { println " " + it } } task.taskDependencies.getDependencies(task).each {…}a }b 'dot -Tpng -O project.dot'.execute(…) }c
  75. gradle.taskGraph

  76. gradle.taskGraph.afterTask { task -> println " inputs: " task.inputs.files.each {

    println " " + it } println " outputs: " task.outputs.files.each { println " " + it } }
  77. > Task :app:checkDebugClasspath inputs: …/gradle-3.2.1.jar …/builder-3.2.1.jar …/tracker-26.2.1.jar …/shared-26.2.1.jar …/crash-26.2.1.jar …

    outputs: …/app/build/intermediates/checkDebugClasspath/debug
  78. Configurations

  79. dependencies { implementation project(‘:api') api project(‘:db’) }a

  80. dependencies { implementation project(‘:api') api project(‘:db’) kapt project(‘:dagger’) testImplementation deps.mockito

    }a
  81. Images from https://jeroenmols.com/blog/2017/06/14/androidstudio3/

  82. Images from https://jeroenmols.com/blog/2017/06/14/androidstudio3/

  83. Images from https://jeroenmols.com/blog/2017/06/14/androidstudio3/

  84. :app dependencies { implementation project(':api') }a

  85. :app dependencies { implementation project(':api') }a :api dependencies { implementation

    deps.retrofit implementation deps.rx2 } @POST("/2.0/cash/register-sms") fun registerSms( @Body request: RegisterSmsRequest ): Observable<RegisterSmsResponse> appService.registerSms( RegisterSmsRequest.Builder() .sms_number(args.sms) .pre_filled_from_phone(true)
  86. appService.registerSms( RegisterSmsRequest.Builder() .sms_number(args.sms) .pre_filled_from_phone(true) :app dependencies { implementation project(':api') }

    :api dependencies { implementation deps.retrofit implementation deps.rx2 } @POST("/2.0/cash/register-sms") fun registerSms( @Body request: RegisterSmsRequest ): Observable<RegisterSmsResponse>
  87. :app dependencies { implementation project(':api') }a :api dependencies { implementation

    deps.retrofit implementation deps.rx2 }a @POST("/2.0/cash/register-sms") fun registerSms( @Body request: RegisterSmsRequest ): Observable<RegisterSmsResponse> appService.registerSms( RegisterSmsRequest.Builder() .sms_number(args.sms) .pre_filled_from_phone(true)
  88. :app dependencies { implementation project(':api') } :api dependencies { implementation

    deps.retrofit api deps.rx2 } @POST("/2.0/cash/register-sms") fun registerSms( @Body request: RegisterSmsRequest ): Observable<RegisterSmsResponse> appService.registerSms( RegisterSmsRequest.Builder() .sms_number(args.sms) .pre_filled_from_phone(true)
  89. :app dependencies { implementation project(':api') }a :api dependencies { implementation

    deps.retrofit api deps.rx2 }b @POST("/2.0/cash/register-sms") fun registerSms( @Body request: RegisterSmsRequest ): Observable<RegisterSmsResponse> appService.registerSms( RegisterSmsRequest.Builder() .sms_number(args.sms) .pre_filled_from_phone(true)
  90. :app dependencies { implementation project(':api') } :api dependencies { api

    deps.retrofit api deps.rx2 } @POST("/2.0/cash/register-sms") fun registerSms( @Body request: RegisterSmsRequest ): Observable<RegisterSmsResponse> appService.registerSms( RegisterSmsRequest.Builder() .sms_number(args.sms) .pre_filled_from_phone(true)
  91. None
  92. None
  93. None
  94. None
  95. None
  96. None
  97. None
  98. None
  99. class AppClasspathCheckTask extends ClasspathComparisionTask { @TaskAction void run() { compareClasspaths();

    } }e
  100. class AppClasspathCheckTask extends ClasspathComparisionTask { void onDifferentVersionsFound(…)a{ String message =

    String.format( "Conflict with dependency '%1$s:%2$s' in project '%3$s'. Resolved versions for runtime classpath (%4$s) and compile classpath (%5$s) differ. This can lead to runtime crashes. To resolve this issue …\n”, …); reporter.reportWarning(EvalIssueReporter.Type.GENERIC, message); } @TaskAction void run() { compareClasspaths(); } }e
  101. class AppClasspathCheckTask extends ClasspathComparisionTask { a{…} }e

  102. class AppClasspathCheckTask extends ClasspathComparisionTask { a{…} class ConfigAction extends TaskConfigAction<AppClasspathCheckTask>

    { String getName() { return variantScope.getTaskName("check", "Classpath"); } void execute(AppClasspathCheckTask task) { task.setVariantName(variantScope.getFullVariantName()); task.runtimeClasspath = variantScope.getArtifactCollection(RUNTIME_CLASSPATH, …); task.compileClasspath = variantScope.getArtifactCollection(COMPILE_CLASSPATH, …); } } }e
  103. None
  104. None
  105. :moduleA dependencies { api libs.retrofit implementation libs.okhttp } :moduleB dependencies

    { implementation libs.retrofit } :app dependencies { implementation project(':module-a') implementation project(':module-b') }
  106. $ ./gradlew app:dependencies debugCompileClasspath: debug +--- project :module-a | \---

    com.squareup.retrofit2:retrofit:2.3.0 | \--- com.squareup.okhttp3:okhttp:3.8.0 | \--- com.squareup.okio:okio:1.13.0 \--- project :module-b debugRuntimeClasspath: debug +--- project :module-a | +--- com.squareup.retrofit2:retrofit:2.3.0 | | \--- com.squareup.okhttp3:okhttp:3.8.0 -> 3.9.0 | | \--- com.squareup.okio:okio:1.13.0 | \--- com.squareup.okhttp3:okhttp:3.9.0 (*) \--- project :module-b \--- com.squareup.retrofit2:retrofit:2.3.0 (*)
  107. $ ./gradlew app:dependencies debugCompileClasspath: debug +--- project :module-a | \---

    com.squareup.retrofit2:retrofit:2.3.0 | \--- com.squareup.okhttp3:okhttp:3.8.0 | \--- com.squareup.okio:okio:1.13.0 \--- project :module-b … debugRuntimeClasspath: debug +--- project :module-a | +--- com.squareup.retrofit2:retrofit:2.3.0 | | \--- com.squareup.okhttp3:okhttp:3.8.0 -> 3.9.0 | | \--- com.squareup.okio:okio:1.13.0 | \--- com.squareup.okhttp3:okhttp:3.9.0 (*) \--- project :module-b \--- com.squareup.retrofit2:retrofit:2.3.0 (*)
  108. None
  109. None
  110. None
  111. None
  112. android { compileSdkVersion 28 defaultConfig { minSdkVersion 27 }b }a

  113. android { compileSdkVersion 28 defaultConfig { minSdkVersion 27 resValue("string", "build_time",

    "0") }b }a
  114. $ ./gradlew android_library:assembleDebug :preBuild -> DefaultTask :preDebugBuild -> DefaultTask :compileDebugAidl

    -> AidlCompile :compileDebugRenderscript -> RenderscriptCompile :checkDebugManifest -> CheckManifest :generateDebugBuildConfig -> GenerateBuildConfig :generateDebugResValues -> GenerateResValuese :generateDebugResources -> DefaultTask :packageDebugResources -> MergeResources :processDebugManifest -> ProcessManifest :generateDebugRFile -> GenerateLibraryRFileTask :prepareLintJar -> PrepareLintJar :generateDebugSources -> DefaultTask :javaPreCompileDebug -> JavaPreCompileTask :compileDebugJavaWithJavac -> AndroidJavaCompile :extractDebugAnnotations -> ExtractAnnotations :mergeDebugConsumerProguardFiles -> MergeFileTask :mergeDebugShaders -> MergeSourceSetFolders :compileDebugShaders -> ShaderCompile
  115. $ ./gradlew android_library:assembleDebug :preBuild -> DefaultTask :preDebugBuild -> DefaultTask :compileDebugAidl

    -> AidlCompile :compileDebugRenderscript -> RenderscriptCompile :checkDebugManifest -> CheckManifest :generateDebugBuildConfig -> GenerateBuildConfig :generateDebugResValues -> GenerateResValuese :generateDebugResources -> DefaultTask :packageDebugResources -> MergeResources :processDebugManifest -> ProcessManifest :generateDebugRFile -> GenerateLibraryRFileTask :prepareLintJar -> PrepareLintJar :generateDebugSources -> DefaultTask :javaPreCompileDebug -> JavaPreCompileTask :compileDebugJavaWithJavac -> AndroidJavaCompile :extractDebugAnnotations -> ExtractAnnotations :mergeDebugConsumerProguardFiles -> MergeFileTask :mergeDebugShaders -> MergeSourceSetFolders :compileDebugShaders -> ShaderCompile
  116. class GenerateResValuese: AndroidBuilderTask { class ConfigAction : TaskConfigAction<GenerateResValues> { fun

    execute(task: GenerateResValues) { scope.taskContainer.generateResValuesTask = task val variantConfiguration = scope.variantData.variantConfiguration task.androidBuilder = scope.globalScope.androidBuilder task.variantName = variantConfiguration.fullName task.items = TaskInputHelper.memoize(variantConfiguration::getResValues) task.resOutputDir = scope.generatedResOutputDir }b }a
  117. @Input fun getItemValues(): List<String> { val resolvedItems = getItems() val

    list = Lists.newArrayListWithCapacity(resolvedItems.size() * 3) for (Object object : resolvedItems) { if (object instanceof String) { list += object } else if (object instanceof ClassField) { val field = object as ClassField list += field.type list += field.name list += field.value } } return list }
  118. @Input fun getItemValues(): List<String> { val resolvedItems = getItems() val

    list = Lists.newArrayListWithCapacity(resolvedItems.size() * 3) for (Object object : resolvedItems) { if (object instanceof String) { list += object } else if (object instanceof ClassField) { val field = object as ClassField list += field.type list += field.name list += field.value } } return list } “string" “build_time" "0"
  119. @TaskAction fun generate() throws IOException, ParserConfigurationException { val folder =

    getResOutputDir() val items = getItems() if (items.isEmpty()) { FileUtils.cleanOutputDir(folder) } else { val generator = new ResValueGenerator(folder) generator.addItems(items) generator.generate() } } }
  120. valAgenerator =Anew generator.addItems(items) generator. generate() ResValueGenerator (folder)

  121. class ResValueGenerator { fun generate() { val resFile = File(pkgFolder,

    “generated.xml”) val factory = DocumentBuilderFactory.newInstance() factory.setNamespaceAware(true) factory.setValidating(false) factory.setIgnoringComments(true) val builder = factory.newDocumentBuilder() val document = builder.newDocument() val rootNode = document.createElement(“resources”) document.appendChild(rootNode)
  122. class ResValueGenerator { fun generate() { val resFile = File(pkgFolder,

    “generated.xml”) val factory = DocumentBuilderFactory.newInstance() factory.setNamespaceAware(true) factory.setValidating(false) factory.setIgnoringComments(true) val builder = factory.newDocumentBuilder() val document = builder.newDocument() val rootNode = document.createElement(“resources”) document.appendChild(rootNode)
  123. $ cat …/android_library/build/generated/res/resValues/debug/values/ generated.xml <?xml version="1.0" encoding="utf-8"?> <resources> <!-- Automatically

    generated file. DO NOT MODIFY --> <!-- Values from default config. --> <string name="build_time" translatable="false">0</string> </resources>
  124. None
  125. None
  126. $ ./gradlew android_library:assembleDebug :preBuild -> DefaultTask :preDebugBuild -> DefaultTask :compileDebugAidl

    -> AidlCompile :compileDebugRenderscript -> RenderscriptCompile :checkDebugManifest -> CheckManifest :generateDebugBuildConfig -> GenerateBuildConfig :generateDebugResValues -> GenerateResValuese :generateDebugResources -> DefaultTask :packageDebugResources -> MergeResources :processDebugManifest -> ProcessManifest :generateDebugRFile -> GenerateLibraryRFileTask :prepareLintJar -> PrepareLintJar :generateDebugSources -> DefaultTask :javaPreCompileDebug -> JavaPreCompileTask :compileDebugJavaWithJavac -> AndroidJavaCompile :extractDebugAnnotations -> ExtractAnnotations :mergeDebugConsumerProguardFiles -> MergeFileTask :mergeDebugShaders -> MergeSourceSetFolders :compileDebugShaders -> ShaderCompile
  127. $ ./gradlew android_library:assembleDebug :preBuild -> DefaultTask :preDebugBuild -> DefaultTask :compileDebugAidl

    -> AidlCompile :compileDebugRenderscript -> RenderscriptCompile :checkDebugManifest -> CheckManifest :generateDebugBuildConfig -> GenerateBuildConfig :generateDebugResValues -> GenerateResValuese :generateDebugResources -> DefaultTask :packageDebugResources -> MergeResources :processDebugManifest -> ProcessManifest :generateDebugRFile -> GenerateLibraryRFileTask :prepareLintJar -> PrepareLintJar :generateDebugSources -> DefaultTask :javaPreCompileDebug -> JavaPreCompileTask :compileDebugJavaWithJavac -> AndroidJavaCompile :extractDebugAnnotations -> ExtractAnnotations :mergeDebugConsumerProguardFiles -> MergeFileTask :mergeDebugShaders -> MergeSourceSetFolders :compileDebugShaders -> ShaderCompile
  128. class MergeResources : IncrementalTask { fun doFullTaskAction() { } }

  129. class MergeResources : IncrementalTask { fun doFullTaskAction() { ResourcePreprocessor preprocessor

    = getPreprocessor(); }2 }1
  130. class MergeResources : IncrementalTask { fun doFullTaskAction() { ResourcePreprocessor preprocessor

    = getPreprocessor(); val resourceCompiler = getResourceCompilationService(…) }2 }1
  131. class MergeResources : IncrementalTask { fun doFullTaskAction() { ResourcePreprocessor preprocessor

    = getPreprocessor(); val resourceCompiler = getResourceCompilationService(…) for (ResourceSet resourceSet : resourceSets) { resourceSet.loadFromFiles(getILogger()); merger.addDataSet(resourceSet); }3 val writer = MergedResourceWriter(…, resourceCompiler, …); merger.mergeData(writer, doCleanUp = false) merger.writeBlobTo(getIncrementalFolder(), writer, false) }2 }1
  132. class MergeResources : IncrementalTask { fun2doFullTaskAction() { ResourcePreprocessor preprocessor =

    getPreprocessor(); val resourceCompiler = get ResourceCompilationService(…) for (ResourceSet resourceSet : resourceSets) { resourceSet.loadFromFiles(getILogger()); merger.addDataSet(resourceSet); }3 val writer = MergedResourceWriter(…, resourceCompiler, …); merger.mergeData(writer, doCleanUp = false) merger.writeBlobTo(getIncrementalFolder(), writer, false) }2 }1
  133. /** Abstraction used by the resource merger. */ interface ResourceCompilationService

    : Closeable { /**qSubmit azrequest. */z @Throws(IOException::class) fun submitCompile(request: CompileResourceRequest) /**q * Given azrequest, returns thezoutput file that would, * will or has been written. */p fun compileOutputFor(request: CompileResourceRequest): File }e
  134. /** Abstraction used by the resource merger. */ interface ResourceCompilationService

    : Closeable { fun submitCompile(request: CompileResourceRequest) fun compileOutputFor(request: CompileResourceRequest): File }e /** Simply copies files to the output directory. */b object CopyToOutputDirectoryResourceCompilationService : ResourceCompilationService /** Built on top of a Aapt2Daemon and Gradle Worker Executors. */ class WorkerExecutorResourceCompilationService : ResourceCompilationService
  135. abstract class Aapt2Daemon(…) : Aapt2

  136. abstract class Aapt2Daemon(…) : Aapt2 class Aapt2DaemonUtil { void requestCompile(Writer

    writer, CompileResourceRequest command) { request(writer, “c", makeCompileCommand(command)); } void requestLink(Writer writer, AaptPackageConfig command) { request(writer, "l", makeLinkCommand(command)); } void requestShutdown(Writer writer) { request(writer, "quit", Collections.emptyList()); }
  137. > Task :android_library:packageDebugResources inputs: …/.gradle/caches/…/aapt2-3.2.1-4818971-osx …/android_lib/build/generated/res/resValues/debug …/android_lib/build/generated/res/rs/debug …/android_lib/src/main/res …/android_lib/src/debug/res outputs:

    …/android_lib/build/generated/res/pngs/debug …/android_lib/b/i/incremental/packageDebugResources …/android_lib/b/i/packaged_res/debug …/android_lib/b/i/public_res/debug/packageDebugResources/public.txt
  138. > Task :android_library:packageDebugResources inputs: …/.gradle/caches/…/aapt2-3.2.1-4818971-osx …/android_lib/build/generated/res/resValues/debug …/android_lib/build/generated/res/rs/debug …/android_lib/src/main/res …/android_lib/src/debug/res outputs:

    …/android_lib/build/generated/res/pngs/debug …/android_lib/b/i/incremental/packageDebugResources …/android_lib/b/i/packaged_res/debug …/android_lib/b/i/public_res/debug/packageDebugResources/public.txt
  139. $ cat …/android_library/src/main/res/**/* <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent"

    android:layout_height=“match_parent"/> <?xml version="1.0" encoding="utf-8"?> <resources> <string name="foo">bar</string> </resources> <?xml version="1.0" encoding=“utf-8"?> <resources> <string name="baz">wut</string> </resources>
  140. $ cat …/android_library/build/intermediates/packaged_res/debug/**/* <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent"

    android:layout_height=“match_parent”/> <?xml version="1.0" encoding="utf-8"?> <resources> <string name="baz">wut</string> <string name="build_time" translatable="false">0</string> <string name="foo">bar</string> </resources>
  141. > Task :app:mergeDebugResources inputs: …/.gradle/caches/…/aapt2-3.2.1-4818971-osx …/app/build/generated/res/resValues/debug …/android_library/build/intermediates/packaged_res/debug …/app/build/generated/res/rs/debug …/app/src/main/res …/app/src/debug/res

    outputs: …/app/build/intermediates/blame/res/debug …/app/build/generated/res/pngs/debug …/app/build/intermediates/incremental/mergeDebugResources …/app/build/intermediates/res/merged/debug
  142. > Task :app:mergeDebugResources inputs: …/.gradle/caches/…/aapt2-3.2.1-4818971-osx …/app/build/generated/res/resValues/debug …/android_library/build/intermediates/packaged_res/debug …/app/build/generated/res/rs/debug …/app/src/main/res …/app/src/debug/res

    outputs: …/app/build/intermediates/blame/res/debug …/app/build/generated/res/pngs/debug …/app/build/intermediates/incremental/mergeDebugResources …/app/build/intermediates/res/merged/debug
  143. $ file -I …/app/build/intermediates/res/merged/debug/* …/app/build/intermediates/res/merged/debug/layout_foo.xml.flat: application/octet-stream; charset=binary …/app/build/intermediates/res/merged/debug/values_values.arsc.flat: application/octet-stream; charset=binary

    $ aapt2 c --legacy -o …/app/build/intermediates/res/merged/debug …/app/ build/intermediates/incremental/mergeDebugResources/merged.dir/values/ values.xml $ aapt2 c --legacy -o …/app/build/intermediates/res/merged/debug …/ android_library/build/intermediates/packaged_res/debug/layout/foo.xml
  144. > Task :app:processDebugResources inputs: …/.gradle/caches/…/aapt2-3.2.1-4818971-osx …/app/b/i/apk_list/debug/mainApkListPersistenceDebug/apk-list.gson …/android_lib/b/i/res/symbol-table-with-package/…/package-aware-r.txt …/app/b/i/res/merged/debug …/app/b/i/split_list/debug/splitsDiscoveryTaskDebug/split-list.gson …/app/b/i/merged_manifests/debug/processDebugManifest/merged

    outputs: …/app/b/i/incremental/processDebugResources …/app/b/i/processed_res/debug/processDebugResources/out …/app/b/i/not_namespaced_r_class_sources/…/processDebugResources/r …/app/b/i/res/symbol-table-with-package/…/package-aware-r.txt …/app/b/i/symbols/debug/R.txt
  145. > Task :app:processDebugResources inputs: …/.gradle/caches/…/aapt2-3.2.1-4818971-osx …/app/b/i/apk_list/debug/mainApkListPersistenceDebug/apk-list.gson …/android_lib/b/i/res/symbol-table-with-package/…/package-aware-r.txt …/app/b/i/res/merged/debug …/app/b/i/split_list/debug/splitsDiscoveryTaskDebug/split-list.gson …/app/b/i/merged_manifests/debug/processDebugManifest/merged

    outputs: …/app/b/i/incremental/processDebugResources …/app/b/i/processed_res/debug/processDebugResources/out …/app/b/i/not_namespaced_r_class_sources/…/processDebugResources/r …/app/b/i/res/symbol-table-with-package/…/package-aware-r.txt …/app/b/i/symbols/debug/R.txt
  146. $ file -I …/app/b…/i…/processed_res/debug/processDebugResources/out/* …/processed_res/debug/processDebugResources/out/output.json: text/plain; charset=us-ascii …/processed_res/debug/processDebugResources/out/resources-debug.ap_: application/zip; charset=binary

    $ aapt2 l -I $ANDROID_HOME/platforms/android-28/android.jar --manifest …/app/b…/i…/merged_manifests/debug/processDebugManifest/merged/AndroidManifest.xml -o …/app/b…/i…/processed_res/debug/processDebugResources/out/resources-debug.ap_ -R …/app/b…/i…/res/merged/debug/layout_foo.xml.flat -R …/app/b…/i…/res/merged/debug/values_values.arsc.flat --auto-add-overlay --java …/app/build/generated/not_namespaced_r_class_sources/debug/processDebugResources/r —-custom-package agp.scratchpad -0 apk --output-text-symbols …/app/b…/i…/symbols/debug/R.txt --no-version-vectors
  147. $ file -I …/app/b…/i…/processed_res/debug/processDebugResources/out/* …/processed_res/debug/processDebugResources/out/output.json: text/plain; charset=us-ascii …/processed_res/debug/processDebugResources/out/resources-debug.ap_: application/zip; charset=binary

    $ aapt2 l -I $ANDROID_HOME/platforms/android-28/android.jar --manifest …/app/b…/i…/merged_manifests/debug/processDebugManifest/merged/AndroidManifest.xml -o …/app/b…/i…/processed_res/debug/processDebugResources/out/resources-debug.ap_ -R …/app/b…/i…/res/merged/debug/layout_foo.xml.flat -R …/app/b…/i…/res/merged/debug/values_values.arsc.flat --auto-add-overlay --java …/app/build/generated/not_namespaced_r_class_sources/debug/processDebugResources/r —-custom-package agp.scratchpad -0 apk --output-text-symbols …/app/b…/i…/symbols/debug/R.txt --no-version-vectors
  148. $ aapt2 c missing required flag -o aapt2 compile [options]

    -o arg files... Options: -o arg Output path --output-text-symbols arg Generates a text file containing the resource symbols in the specified file --no-crunch Disables PNG processing --legacy Treat errors that used to be valid in AAPT as warnings -v Enables verbose logging -h Displays this help menu
  149. Resource processing • Before in aapt: take all resources and

    outputs one binary • Now in aapt2: compile + link • converts individual resources into binary flat files • merge at the end • uses Gradle incremental check feature for compile avoidance • Improved mergeReleaseResources for libraries • avoids full merge of resources for all the transitive dependencies
  150. None
  151. None
  152. $ ./gradlew android_library:assembleDebug :preBuild -> DefaultTask :preDebugBuild -> DefaultTask :compileDebugAidl

    -> AidlCompile :compileDebugRenderscript -> RenderscriptCompile :checkDebugManifest -> CheckManifest :generateDebugBuildConfig -> GenerateBuildConfig :generateDebugResValues -> GenerateResValuese :generateDebugResources -> DefaultTask :packageDebugResources -> MergeResources :processDebugManifest -> ProcessManifest :generateDebugRFile -> GenerateLibraryRFileTask :prepareLintJar -> PrepareLintJar :generateDebugSources -> DefaultTask :javaPreCompileDebug -> JavaPreCompileTask :compileDebugJavaWithJavac -> AndroidJavaCompile :extractDebugAnnotations -> ExtractAnnotations :mergeDebugConsumerProguardFiles -> MergeFileTask :mergeDebugShaders -> MergeSourceSetFolders :compileDebugShaders -> ShaderCompile
  153. $ ./gradlew android_library:assembleDebug :preBuild -> DefaultTask :preDebugBuild -> DefaultTask :compileDebugAidl

    -> AidlCompile :compileDebugRenderscript -> RenderscriptCompile :checkDebugManifest -> CheckManifest :generateDebugBuildConfig -> GenerateBuildConfig :generateDebugResValues -> GenerateResValuese :generateDebugResources -> DefaultTask :packageDebugResources -> MergeResources :processDebugManifest -> ProcessManifest :generateDebugRFile -> GenerateLibraryRFileTask :prepareLintJar -> PrepareLintJar :generateDebugSources -> DefaultTask :javaPreCompileDebug -> JavaPreCompileTask :compileDebugJavaWithJavac -> AndroidJavaCompile :extractDebugAnnotations -> ExtractAnnotations :mergeDebugConsumerProguardFiles -> MergeFileTask :mergeDebugShaders -> MergeSourceSetFolders :compileDebugShaders -> ShaderCompile
  154. class MergeManifests : IncrementalTask { }

  155. class MergeManifests : IncrementalTask { fun doIncrementalTaskAction(changedInputs: Map<File, FileStatus>) {

    // do nothing. } }
  156. class ManifestMerger2 { /** * Perform high level ordering of

    files merging and delegates * actual merging to * {@link XmlDocument#merge(XmlDocument, MergingReport.Builder)} */ fun merge(): MergingReport { val mergingReportBuilder = MergingReport.Builder(mLogger, this) val loadedMainManifestInfo = load(…, mergingReportBuilder) val loadedLibraryDocuments = loadLibraries(…) // invariant : xmlDocumentOptional holds the higher priority // document and we try to merge in lower priority documents. Optional<XmlDocument> xmlDocumentOptional = Optional.absent(); for (File inputFile : mFlavorsAndBuildTypeFiles) {
  157. // invariant : xmlDocumentOptional holds the higher priority // document

    and we try to merge in lower priority documents. Optional<XmlDocument> xmlDocumentOptional = Optional.absent(); for (File inputFile : mFlavorsAndBuildTypeFiles) { https://developer.android.com/studio/build/manifest-merge
  158. > Task :android_library:processDebugManifest inputs: …/lib/src/main/AndroidManifest.xml outputs: …/lib/b…/i…/aapt_friendly_…_manifests/…/processDebugManifest/aapt …/lib/b…/i…/merged_manifests/…/processDebugManifest/merged …/lib/build/outputs/logs/manifest-merger-debug-report.txt

  159. $ cat …/android_lib/build/outputs/logs/manifest-merger-debug-report.txt -- Merging decision tree log --- manifest

    ADDED from …/android_library/src/main/AndroidManifest.xml:1:1-2:41 INJECTED from …/android_library/src/main/AndroidManifest.xml:1:1-2:41 INJECTED from …/android_library/src/main/AndroidManifest.xml:1:1-2:41 package ADDED from …/android_library/src/main/AndroidManifest.xml:2:5-39 INJECTED from …/android_library/src/main/AndroidManifest.xml INJECTED from …/android_library/src/main/AndroidManifest.xml xmlns:android ADDED from …/android_library/src/main/AndroidManifest.xml:1:1-2:41 uses-sdk INJECTED from …/android_library/src/main/AndroidManifest.xml reason: use- sdk injection requested INJECTED from …/android_library/src/main/AndroidManifest.xml INJECTED from …/android_library/src/main/AndroidManifest.xml android:minSdkVersion INJECTED from /Users/jrod/Development/AGPScratchpad/android_library/ src/main/AndroidManifest.xml
  160. /** * Optional behavior of the merging tool can be

    turned on by * setting these Features. */ public enum Feature { /** * Keep all intermediary merged files during the merging process. * This is particularly useful for debugging/tracing purposes. */ KEEP_INTERMEDIARY_STAGES, /** * When logging file names, use {@link java.io.File#getName()} * rather than {@link java.io.File#getPath()} */ PRINT_SIMPLE_FILENAMES, /** * Perform a sweep after all merging activities to remove all
  161. * Add android:testOnly="true" attribute to prevent APK from * being

    uploaded to Play store. */ TEST_ONLY, /** * Do not perform implicit permission addition. */ NO_IMPLICIT_PERMISSION_ADDITION, /** Perform Studio advanced profiling manifest modifications */ ADVANCED_PROFILING, /** Mark this application as a feature split */ ADD_FEATURE_SPLIT_ATTRIBUTE, /** Mark the entry points to the application with splitName */ ADD_INSTANT_APP_FEATURE_SPLIT_INFO, /** Set the android:debuggable flag to the application. */ DEBUGGABLE, /** Set the android:targetSandboxVersion attribute. */
  162. ADD_INSTANT_APP_FEATURE_SPLIT_INFO, /** Set the android:debuggable flag to the application. */

    DEBUGGABLE, /** Set the android:targetSandboxVersion attribute. */ TARGET_SANDBOX_VERSION, /** * When there are attribute value conflicts, automatically pick * the higher priority value. */ HANDLE_VALUE_CONFLICTS_AUTOMATICALLY, /** * Adds AndroidX name {@link SdkConstants#MULTI_DEX_APPLICATION} * as application name if none is specified. Used for legacy * multidex. */ ADD_ANDROIDX_MULTIDEX_APPLICATION_IF_NO_NAME, /** * Adds pre-AndroidX name {@link SdkConstants#MULTI_DEX_APPLICATION} * as application name if none is specified. Used for legacy
  163. None
  164. None
  165. $ ./gradlew android_library:assembleDebug :preBuild -> DefaultTask :preDebugBuild -> DefaultTask :compileDebugAidl

    -> AidlCompile :compileDebugRenderscript -> RenderscriptCompile :checkDebugManifest -> CheckManifest :generateDebugBuildConfig -> GenerateBuildConfig :generateDebugResValues -> GenerateResValuese :generateDebugResources -> DefaultTask :packageDebugResources -> MergeResources :processDebugManifest -> ProcessManifest :generateDebugRFile -> GenerateLibraryRFileTask :prepareLintJar -> PrepareLintJar :generateDebugSources -> DefaultTask :javaPreCompileDebug -> JavaPreCompileTask :compileDebugJavaWithJavac -> AndroidJavaCompile :extractDebugAnnotations -> ExtractAnnotations :mergeDebugConsumerProguardFiles -> MergeFileTask :mergeDebugShaders -> MergeSourceSetFolders :compileDebugShaders -> ShaderCompile
  166. $ ./gradlew android_library:assembleDebug :preBuild -> DefaultTask :preDebugBuild -> DefaultTask :compileDebugAidl

    -> AidlCompile :compileDebugRenderscript -> RenderscriptCompile :checkDebugManifest -> CheckManifest :generateDebugBuildConfig -> GenerateBuildConfig :generateDebugResValues -> GenerateResValuese :generateDebugResources -> DefaultTask :packageDebugResources -> MergeResources :processDebugManifest -> ProcessManifest :generateDebugRFile -> GenerateLibraryRFileTask :prepareLintJar -> PrepareLintJar :generateDebugSources -> DefaultTask :javaPreCompileDebug -> JavaPreCompileTask :compileDebugJavaWithJavac -> AndroidJavaCompile :extractDebugAnnotations -> ExtractAnnotations :mergeDebugConsumerProguardFiles -> MergeFileTask :mergeDebugShaders -> MergeSourceSetFolders :compileDebugShaders -> ShaderCompile
  167. class GenerateLibraryRFileTask : ProcessAndroidResources() { class ConfigAction( private val variantScope:

    VariantScope, private val symbolFile: File, private val symbolsWithPackageNameOutputFile: File ) : TaskConfigAction<GenerateLibraryRFileTask> { fun execute(task: GenerateLibraryRFileTask) { variantScope.taskContainer.processAndroidResTask = task } } }
  168. > Task :android_library:generateDebugRFile inputs: …/android_library/build/intermediates/packaged_res/debug …/.gradle/caches/…/android.jar/…/R.txt …/lib/b…/i…/merged_manifests/debug/processDebugManifest/merged outputs: …/lib/b…/generated/not_namespaced_r_…/debug/generateDebugRFile/out …/lib/b…/i…/res/symbol-table-with-package/…/package-aware-r.txt

    …/lib/b…/i…/symbols/debug/R.txt
  169. None
  170. None
  171. None
  172. None
  173. android { compileSdkVersion 28 defaultConfig { minSdkVersion 27 resValue("string", "build_time",

    "0") }b }a
  174. android { compileSdkVersion 28 defaultConfig { minSdkVersion 27 buildConfigField("String", "BUILD_TIME",

    "\"0\"") resValue("string", "build_time", "0") }b }a
  175. $ ./gradlew android_library:assembleDebug :preBuild -> DefaultTask :preDebugBuild -> DefaultTask :compileDebugAidl

    -> AidlCompile :compileDebugRenderscript -> RenderscriptCompile :checkDebugManifest -> CheckManifest :generateDebugBuildConfig -> GenerateBuildConfig :generateDebugResValues -> GenerateResValuese :generateDebugResources -> DefaultTask :packageDebugResources -> MergeResources :processDebugManifest -> ProcessManifest :generateDebugRFile -> GenerateLibraryRFileTask :prepareLintJar -> PrepareLintJar :generateDebugSources -> DefaultTask :javaPreCompileDebug -> JavaPreCompileTask :compileDebugJavaWithJavac -> AndroidJavaCompile :extractDebugAnnotations -> ExtractAnnotations :mergeDebugConsumerProguardFiles -> MergeFileTask :mergeDebugShaders -> MergeSourceSetFolders :compileDebugShaders -> ShaderCompile
  176. class GenerateBuildConfig : AndroidBuilderTask { @TaskAction fun generate() {…}a }b

  177. class GenerateBuildConfig : AndroidBuilderTask { @TaskAction fun generate() { valzgenerator

    =z BuildConfigGenerator (sourceOutputDir, buildConfigPackageName) generator .addField(“boolean", “DEBUG", debuggable ? "true" : "false")z .addField("String", "APPLICATION_ID", “\”$appPackageName\"") .addField("String", "BUILD_TYPE", “\”$buildTypeName\"") .addField("String", "FLAVOR", “\”$flavorName\"") .addField("int", "VERSION_CODE", versionCode) .addField("String", "VERSION_NAME", “\”$versionName\"") .addItems(items) generator. generate ( )z }a }b
  178. $ cat …/android_library/build/generated/source/buildConfig/debug/…/ BuildConfig.java /** * Automatically generated file. DO

    NOT MODIFY */ public final class BuildConfig { public static final boolean DEBUG = Boolean.parseBoolean("true"); public static final String APPLICATION_ID = “…”; public static final String BUILD_TYPE = "debug"; public static final String FLAVOR = ""; public static final int VERSION_CODE = -1; public static final String VERSION_NAME = ""; // Fields from default config. public static final String BUILD_TIME = "0"; }
  179. None
  180. None
  181. $ ./gradlew android_library:assembleDebug :preBuild -> DefaultTask :preDebugBuild -> DefaultTask :compileDebugAidl

    -> AidlCompile :compileDebugRenderscript -> RenderscriptCompile :checkDebugManifest -> CheckManifest :generateDebugBuildConfig -> GenerateBuildConfig :generateDebugResValues -> GenerateResValuese :generateDebugResources -> DefaultTask :packageDebugResources -> MergeResources :processDebugManifest -> ProcessManifest :generateDebugRFile -> GenerateLibraryRFileTask :prepareLintJar -> PrepareLintJar :generateDebugSources -> DefaultTask :javaPreCompileDebug -> JavaPreCompileTask :compileDebugJavaWithJavac -> AndroidJavaCompile :extractDebugAnnotations -> ExtractAnnotations :mergeDebugConsumerProguardFiles -> MergeFileTask :mergeDebugShaders -> MergeSourceSetFolders :compileDebugShaders -> ShaderCompile
  182. class PrepareLintJar : DefaultTaska{ static class ConfigAction : TaskConfigAction<PrepareLintJar> {

    fun execute(PrepareLintJar task) { task.outputLintJar = scope.artifacts .appendArtifact(InternalArtifactType.LINT_JAR, task, FN_LINT_JAR) task.lintChecks = scope. getLocalCustomLintChecks() } } }
  183. None
  184. None
  185. None
  186. None
  187. apply plugin: 'com.android.application'
 
 android {
 lintOptions {…}
 }
 


    dependencies {
 lintChecks project(':lint')
 }
  188. class PrepareLintJar : DefaultTaska{ static class ConfigAction : TaskConfigAction<PrepareLintJar> {

    fun execute(PrepareLintJar task) { task.outputLintJar = scope.artifacts .appendArtifact(InternalArtifactType.LINT_JAR, task, FN_LINT_JAR) task.lintChecks = scope. getLocalCustomLintChecks() } } }
  189. class PrepareLintJar : DefaultTaska{ static class ConfigAction : TaskConfigAction<PrepareLintJar> {

    fun execute(PrepareLintJar task) { task.outputLintJar = scope.artifacts .appendArtifact(InternalArtifactType.LINT_JAR, task, FN_LINT_JAR) task.lintChecks = scope. getLocalCustomLintChecks() } } }
  190. classaGlobalScope { afun getLocalCustomLintChecks() : FileCollection { val attributes =z

    container -> container.attribute(ARTIFACT_TYPE, ArtifactType.JAR.getType()) return lintChecks .getIncoming() .artifactView(config -> config.attributes(attributes)) .getArtifacts() .getArtifactFiles(); } }
  191. classaGlobalScope { afun getLocalCustomLintChecks() : FileCollection { val attributes =z

    container -> container.attribute(ARTIFACT_TYPE, ArtifactType.JAR.getType()) return lintChecks .getIncoming() .artifactView(config -> config.attributes(attributes)) .getArtifacts() .getArtifactFiles(); } }
  192. classaGlobalScope { afun getLocalCustomLintChecks() : FileCollection { val attributes =z

    container -> container.attribute(ARTIFACT_TYPE, ArtifactType.JAR.getType()) return lintChecks .getIncoming() .artifactView(config -> config.attributes(attributes)) .getArtifacts() .getArtifactFiles(); } }
  193. None
  194. http://bit.ly/artifact-transform-api

  195. android.libraryVariants.all { variant -> def runtimeConfiguration = variant.getRuntimeConfiguration() def attributes

    = container -> container.attribute(ARTIFACT_TYPE, “android-assets“) runtimeConfiguration.getIncoming() .artifactView(config -> config.attributes(attributes) .artifacts.artifacts.each { result -> // This file is an asset def file = result.file } }
  196. android.libraryVariants.all { variant -> def runtimeConfiguration = variant.getRuntimeConfiguration() def attributes

    = container -> container.attribute(ARTIFACT_TYPE, “android-assets“) runtimeConfiguration.getIncoming() .artifactView(config -> config.attributes(attributes) .artifacts.artifacts.each { result -> // This file is an asset def file = result.file } }
  197. def assets = container -> container.attribute(ARTIFACT_TYPE, “android-assets“) task “process${variant}Assets” {

    inputs variant .getRuntimeConfiguration() .getIncoming() .artifactView(config -> config.attributes(assets) .artifacts.artifacts.each { result -> // This file is an asset def file = result.file } }
  198. None
  199. None
  200. > Task :app:transformClassesWithDexBuilderForDebug inputs: …/.gradle/caches/…/converter-moshi-2.4.0.jar …/.gradle/caches/…/adapter-rxjava2-2.4.0.jar …/.gradle/caches/…/retrofit-2.4.0.jar …/.gradle/caches/…/rxjava-2.2.3.jar …/.gradle/caches/…/okhttp-3.10.0.jar …/java_library/build/libs/java_library.jar

    …/android_library/build/intermediates/…/classes.jar …/app/build/intermediates/…/classes/…/android_library/R.class …/app/build/intermediates/…/classes/…/R$string.class …/app/build/intermediates/…/classes/…/R$layout.class …/app/build/intermediates/…/classes/…/R.class …/app/build/intermediates/…/classes/…/BuildConfig.class … outputs: …/app/build/intermediates/transforms/dexBuilder/debug
  201. • D8 - new dex compiler, replaced DX • R8

    - new optimizer, will replace Proguard
 • Compared to DX, D8 compiles faster and outputs smaller .dex files, while having the same or better app runtime performance.
 • Set to default in 3.1. • Jetifier - opt-in transform that migrates support-library-dependent libraries to rely on the equivalent AndroidX packages instead. Transform API
  202. None
  203. None
  204. Improving your Builds

  205. enum class BooleanOption( override val propertyName: String, override val defaultValue:

    Boolean = false, override val status: Option.Status = Option.Status.EXPERIMENTAL, override val additionalInfo: String = "" ) : Option<Boolean> { ENABLE_AAPT2("android.enableAapt2", true, DeprecationReporter.DeprecationTarget.AAPT), ENABLE_BUILD_CACHE("android.enableBuildCache", true), ENABLE_PROFILE_JSON("android.enableProfileJson", false), // Used by Studio as workaround for b/71054106, b/75955471 ENABLE_SDK_DOWNLOAD("android.builder.sdkDownload", true, status = Option.Status.STABLE), ENABLE_TEST_SHARDING("android.androidTest.shardBetweenDevices"), ENABLE_DEX_ARCHIVE( "android.useDexArchive", true, aDeprecationReporter.DeprecationTarget.LEGACY_DEXER ),a
  206. VERSION_CHECK_OVERRIDE_PROPERTY("android.overrideVersionCheck"), OVERRIDE_PATH_CHECK_PROPERTY("android.overridePathCheck"), ENABLE_DESUGAR( "android.enableDesugar", true, DeprecationReporter.DeprecationTarget.DESUGAR_TOOL), ENABLE_INCREMENTAL_DESUGARING( “android.enableIncrementalDesugaring", true, DeprecationReporter.DeprecationTarget.INCREMENTAL_DESUGARING),

    ENABLE_GRADLE_WORKERS("android.enableGradleWorkers", false), ENABLE_AAPT2_WORKER_ACTIONS( “android.enableAapt2WorkerActions", true), ENABLE_CORE_LAMBDA_STUBS( "android.enableCoreLambdaStubs", true, DeprecationReporter.DeprecationTarget.CORE_LAMBDA_STUBS), ENABLE_D8("android.enableD8", true, DeprecationReporter.DeprecationTarget.LEGACY_DEXER),
  207. AndroidProject.PROPERTY_REFRESH_EXTERNAL_NATIVE_MODEL, status = Option.Status.STABLE), IDE_GENERATE_SOURCES_ONLY( AndroidProject.PROPERTY_GENERATE_SOURCES_ONLY, status = Option.Status.STABLE), ENABLE_SEPARATE_APK_RESOURCES("android.enableSeparateApkRes",

    true), ENABLE_EXPERIMENTAL_FEATURE_DATABINDING( "android.enableExperimentalFeatureDatabinding", false), ENABLE_SEPARATE_R_CLASS_COMPILATION( "android.enableSeparateRClassCompilation"), ENABLE_JETIFIER("android.enableJetifier", false, status = Option.Status.STABLE), USE_ANDROID_X("android.useAndroidX", false, status = Option.Status.STABLE), ENABLE_UNIT_TEST_BINARY_RESOURCES( "android.enableUnitTestBinaryResources", false), DISABLE_EARLY_MANIFEST_PARSING(
  208. ~/.gradle/gradle.properties org.gradle.caching=true

  209. ~/.gradle/gradle.properties org.gradle.caching=true …/build.gradle kapt { useBuildCache true }

  210. ~/.gradle/gradle.properties org.gradle.caching=true …/build.gradle kapt { useBuildCache !isCi }

  211. variantFilter { }a

  212. variantFilter { variant -> if (variant.flavors[0].name == 'production' && variant.buildType.name

    == 'debug') { variant.ignore = true } }a
  213. $ ./gradlew app:assembleDebug --profile

  214. None
  215. None
  216. None
  217. None
  218. None
  219. None
  220. None
  221. None
  222. None
  223. None
  224. None
  225. http://bit.ly/sdk-search-graph

  226. @jrodbx Deeper Dive into the Android Gradle Plugin