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. • Initialization • Configuration • Execution Build phases rootProject.name =

    'cash' include ':analytics' include ':app' include ':db' include ':keypad' include ':presenters' include ':protos' include ':screens' include ':viewmodels'
  2. • 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
  3. • Initialization • Configuration • Execution Build phases project('cash') project(':analytics')

    project(':db') project(':app') project(':keypad') project(':presenters') project(':protos') project(':screens') project(':viewmodels')
  4. • 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
  5. • 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
  6. Configuration buildscript { repositories { google() jcenter() }d dependencies {

    classpath 'com.android.tools.build:gradle:3.2.1' }f }e
  7. 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
  8. 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
  9. 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
  10. 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
  11. 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
  12. 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
  13. 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
  14. apply plugin: GreetingPlugin greeting { message 'Hi' greeter 'Gradle' }

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

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

    List<File> getInputFiles() {…} @TaskAction void doStuff() {…}
  17. allprojectsa{ project -> project.tasks.whenTaskAdded { task -> println "added task:

    " + task.name } afterEvaluate { project -> println "just evaluated: $project" } }a
  18. > 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
  19. 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') } }
  20. productFlavors { flavorDimensions 'environment' internal { dimension 'environment' applicationId 'com.squareup.cash.beta'

    } production { dimension 'environment' applicationId 'com.squareup.cash' } } debug release
  21. $ ./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
  22. ?

  23. 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
  24. 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
  25. 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
  26. 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
  27. 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
  28. 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
  29. 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
  30. 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
  31. 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
  32. 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
  33. gradle.taskGraph.afterTask { task -> println " inputs: " task.inputs.files.each {

    println " " + it } println " outputs: " task.outputs.files.each { println " " + it } }
  34. :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)
  35. 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>
  36. :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)
  37. :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)
  38. :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)
  39. :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)
  40. 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
  41. 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
  42. :moduleA dependencies { api libs.retrofit implementation libs.okhttp } :moduleB dependencies

    { implementation libs.retrofit } :app dependencies { implementation project(':module-a') implementation project(':module-b') }
  43. $ ./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 (*)
  44. $ ./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 (*)
  45. $ ./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
  46. $ ./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
  47. 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
  48. @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 }
  49. @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"
  50. @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() } } }
  51. 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)
  52. 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)
  53. $ 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>
  54. $ ./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
  55. $ ./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
  56. class MergeResources : IncrementalTask { fun doFullTaskAction() { ResourcePreprocessor preprocessor

    = getPreprocessor(); val resourceCompiler = getResourceCompilationService(…) }2 }1
  57. 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
  58. 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
  59. /** 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
  60. /** 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
  61. 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()); }
  62. > 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
  63. > 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
  64. $ 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>
  65. $ 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>
  66. > 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
  67. > 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
  68. $ 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
  69. > 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
  70. > 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
  71. $ 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
  72. $ 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
  73. $ 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
  74. 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
  75. $ ./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
  76. $ ./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
  77. 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) {
  78. // 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
  79. $ 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
  80. /** * 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
  81. * 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. */
  82. 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
  83. $ ./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
  84. $ ./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
  85. 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 } } }
  86. $ ./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
  87. 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
  88. $ 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"; }
  89. $ ./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
  90. 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() } } }
  91. 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() } } }
  92. 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() } } }
  93. 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(); } }
  94. 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(); } }
  95. 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(); } }
  96. 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 } }
  97. 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 } }
  98. 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 } }
  99. > 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
  100. • 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
  101. 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
  102. 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),
  103. 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(