Share
Embed
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
Transcript
226 slides
-
• Initialization • Configuration • Execution Build phases rootProject.name =
'cash' include ':analytics' include ':app' include ':db' include ':keypad' include ':presenters' include ':protos' include ':screens' include ':viewmodels' -
• 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 -
• Initialization • Configuration • Execution Build phases project('cash') project(':analytics')
project(':db') project(':app') project(':keypad') project(':presenters') project(':protos') project(':screens') project(':viewmodels') -
• 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 -
• 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 -
Configuration buildscript { repositories { google() jcenter() }d dependencies {
classpath 'com.android.tools.build:gradle:3.2.1' }f }e -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
apply plugin: GreetingPlugin greeting { message 'Hi' greeter 'Gradle' }
$ ./gradlew Greeting > Task :Greeting Hi from Gradle -
interface Project extends ExtensionAware, PluginAware { } interface ExtensionAware {
ExtensionContainer getExtensions(); } interface PluginAware { void apply(…); PluginManager getPluginManager(); } -
class SomeTask extends DefaultTask { @OutputDirectory File getOutputDir() {…} @InputFiles
List<File> getInputFiles() {…} @TaskAction void doStuff() {…} -
Notifications project.tasks.whenTaskAdded {} project.beforeEvaluate {} project.afterEvaluate {} gradle.projectsEvaluated {} gradle.taskGraph.whenReady
{} gradle.taskGraph.beforeTask {} gradle.taskGraph.afterTask {} -
allprojectsa{ project -> project.tasks.whenTaskAdded { task -> println "added task:
" + task.name } afterEvaluate { project -> println "just evaluated: $project" } }a -
> 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 -
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') } } -
productFlavors { flavorDimensions 'environment' internal { dimension 'environment' applicationId 'com.squareup.cash.beta'
} production { dimension 'environment' applicationId 'com.squareup.cash' } } debug release -
$ ./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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
gradle.taskGraph.afterTask { task -> println " inputs: " task.inputs.files.each {
println " " + it } println " outputs: " task.outputs.files.each { println " " + it } } -
> 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 -
: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) -
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> -
: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) -
: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) -
: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) -
: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) -
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 -
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 -
:moduleA dependencies { api libs.retrofit implementation libs.okhttp } :moduleB dependencies
{ implementation libs.retrofit } :app dependencies { implementation project(':module-a') implementation project(':module-b') } -
$ ./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 (*) -
$ ./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 (*) -
$ ./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 -
$ ./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 -
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 -
@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 } -
@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" -
@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() } } } -
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) -
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) -
$ 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> -
$ ./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 -
$ ./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 -
class MergeResources : IncrementalTask { fun doFullTaskAction() { ResourcePreprocessor preprocessor
= getPreprocessor(); }2 }1 -
class MergeResources : IncrementalTask { fun doFullTaskAction() { ResourcePreprocessor preprocessor
= getPreprocessor(); val resourceCompiler = getResourceCompilationService(…) }2 }1 -
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 -
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 -
/** 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 -
/** 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 -
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()); } -
> 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 -
> 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 -
$ 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> -
$ 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> -
> 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 -
> 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 -
$ 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 -
> 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 -
> 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 -
$ 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 -
$ 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 -
$ 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 -
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 -
$ ./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 -
$ ./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 -
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) { -
// 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 -
$ 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 -
/** * 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 -
* 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. */ -
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 -
$ ./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 -
$ ./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 -
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 } } } -
android { compileSdkVersion 28 defaultConfig { minSdkVersion 27 buildConfigField("String", "BUILD_TIME",
"\"0\"") resValue("string", "build_time", "0") }b }a -
$ ./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 -
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 -
$ 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"; } -
$ ./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 -
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() } } } -
apply plugin: 'com.android.application' android { lintOptions {…} }
dependencies { lintChecks project(':lint') } -
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() } } } -
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() } } } -
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(); } } -
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(); } } -
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(); } } -
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 } } -
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 } } -
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 } } -
> 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 -
• 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 -
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 -
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), -
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( -
variantFilter { variant -> if (variant.flavors[0].name == 'production' && variant.buildType.name
== 'debug') { variant.ignore = true } }a