An overview of the
I started a new series. The goal of this series of learning Gradle is to thoroughly understand Gradle. The main goal is to make notes on your own understanding to prevent forgetting
Gradle Series (1) : Groovy learning
Gradle Learning series (2) : Gradle core decryption
Gradle Plugins
Gradle dependencies
Gradle Transform. Gradle Transform
Gradle learning series (6) : Gradle source code parsing
Android Gradle Plugin source code parsing
Preparing for Reading
First of all, be sure to read this article Gradle Learning series (3) : Gradle plug-in, this is the foundation, first try to customize a plug-in, and then look at the source code will be simpler
Prepare the source code
A handy way to look at the source code is to first set up a Java libary and then change the build to the code below
apply plugin: 'groovy' / / must
apply plugin: 'maven' // This plugin must be used in order to publish to Maven
dependencies {
implementation gradleApi() / / must
implementation localGroovy() / / must
implementation 'com. Android. Tools. Build: gradle: 3.4.1 track'
}
repositories {
mavenCentral() / / must
}
Copy the code
Build and you can see the gradle source code in External Libraries
Start reading the source code
What to look for first? Of course, we often use to find the plug-in, see his source code implementation, we often use the plug-in is this
apply plugin: 'com.android.application'
Copy the code
How to find the source code of this plug-in, this needs the basic knowledge of the plug-in, do not know the return to see Gradle learning series (3) : Gradle plug-in
Directly see the implementation class is AppPlugin, and then continue to look at the source of AppPlugin
public class AppPlugin extends AbstractAppPlugin {
@Inject
public AppPlugin(ToolingModelBuilderRegistry registry) {
super(registry, true /*isBaseApplication*/); }...@Override
@NonNull
protectedClass<? extends AppExtension> getExtensionClass() { return BaseAppModuleExtension.class; }}Copy the code
We know that the plug-in class needs to implement Plugin
public abstract class AbstractAppPlugin extends BasePlugin<AppExtensionImpl> {
private final booleanisBaseApplication; .@NonNull
@Override
protected BaseExtension createExtension(
@NonNull Project project,
@NonNull ProjectOptions projectOptions,
@NonNull GlobalScope globalScope,
@NonNull SdkHandler sdkHandler,
@NonNull NamedDomainObjectContainer<BuildType> buildTypeContainer,
@NonNull NamedDomainObjectContainer<ProductFlavor> productFlavorContainer,
@NonNull NamedDomainObjectContainer<SigningConfig> signingConfigContainer,
@NonNull NamedDomainObjectContainer<BaseVariantOutput> buildOutputs,
@NonNull SourceSetManager sourceSetManager,
@NonNull ExtraModelInfo extraModelInfo) {
return project.getExtensions()
.create(
"android", getExtensionClass(), project, projectOptions, globalScope, sdkHandler, buildTypeContainer, productFlavorContainer, signingConfigContainer, buildOutputs, sourceSetManager, extraModelInfo, isBaseApplication); }}Copy the code
AbstractAppPlugin inherits from BasePlugin, so keep looking
public abstract class BasePlugin<E extends BaseExtension2>
implements Plugin<Project>, ToolingRegistryProvider {.@Override
public final void apply(@NonNull Project project) {
CrashReporting.runAction(
() -> {
basePluginApply(project);
pluginSpecificApply(project);
});
}
Copy the code
We see that BasePlugin implements Plugin
private void basePluginApply(@NonNull Project project) {
// There are some initialization operations sitting on top.if(! projectOptions.get(BooleanOption.ENABLE_NEW_DSL_AND_API)) {// Here are the main methods
threadRecorder.record(
ExecutionType.BASE_PLUGIN_PROJECT_CONFIGURE,
project.getPath(),
null.// Configure the project
this::configureProject);
threadRecorder.record(
ExecutionType.BASE_PLUGIN_PROJECT_BASE_EXTENSION_CREATION,
project.getPath(),
null.// Configure the extension
this::configureExtension);
threadRecorder.record(
ExecutionType.BASE_PLUGIN_PROJECT_TASKS_CREATION,
project.getPath(),
null.// Create a task
this::createTasks); }}Copy the code
Now let’s look at each of these methods at once
configureProject
private void configureProject() {
...
// The SDK that handles everything for the Gradle plugin. There is about one instance per project
sdkHandler = new SdkHandler(project, getLogger());
// Create AndroidBuilder, which is the main builder class. It gets all the data to process the build (for example, {@link
//DefaultProductFlavor}, {@link DefaultBuildType}, and dependencies)
AndroidBuilder androidBuilder =
new AndroidBuilder(
project == project.getRootProject() ? project.getName() : project.getPath(),
creator,
new GradleProcessExecutor(project),
new GradleJavaProcessExecutor(project),
extraModelInfo.getSyncIssueHandler(),
extraModelInfo.getMessageReceiver(),
getLogger());
// Create dataBindingBuilder,
dataBindingBuilder = newDataBindingBuilder(); dataBindingBuilder.setPrintMachineReadableOutput( SyncOptions.getErrorFormatMode(projectOptions) == ErrorFormatMode.MACHINE_PARSABLE); .// Enforce minimum versions of certain plugins
// Force the use of at least the minimum version of the currently supported plug-in
GradlePluginUtils.enforceMinimumVersionsOfPlugins(
project, androidBuilder.getIssueReporter());
// Apply the Java plugin
// Apply the Java plug-in
project.getPlugins().apply(JavaBasePlugin.class);
// Create build cache. If enabled, the build cache directory is set to the user-defined directory, or the default directory if the user-defined directory is not provided
@Nullable
FileCache buildCache = BuildCacheUtils.createBuildCacheIfEnabled(project, projectOptions);
// Create a global scope to save data for the Android Plugin
globalScope =
new GlobalScope(
project,
new ProjectWrapper(project),
projectOptions,
dslScope,
androidBuilder,
sdkHandler,
registry,
buildCache);
// Go to assemble Task and add the description
project.getTasks()
.getByName("assemble")
.setDescription(
"Assembles all variants of all applications and secondary packages.");
// Callback at execution. This is called after the entire build is complete (not after the current project is complete).
// This will be called for every (Android) project, so this should support being called 2+ times.
gradle.addBuildListener(
new BuildListener() {
@Override
public void buildStarted(@NonNull Gradle gradle) {}
@Override
public void settingsEvaluated(@NonNull Settings settings) {}
@Override
public void projectsLoaded(@NonNull Gradle gradle) {}
@Override
public void projectsEvaluated(@NonNull Gradle gradle) {}
@Override
public void buildFinished(@NonNull BuildResult buildResult) {
// Do not run buildFinished for included project in composite build.
if(buildResult.getGradle().getParent() ! =null) {
return;
}
ModelBuilder.clearCaches();
sdkHandler.unload();
threadRecorder.record(
ExecutionType.BASE_PLUGIN_BUILD_FINISHED,
project.getPath(),
null, () -> { WorkerActionServiceRegistry.INSTANCE .shutdownAllRegisteredServices( ForkJoinPool.commonPool()); Main.clearInternTables(); }); DeprecationReporterImpl.Companion.clean(); }}); }Copy the code
This method basically does a few things
- Create SdkHandler, an SDK that handles everything for the Gradle plug-in. There is about one instance per project
- Create AndroidBuilder, which is the main builder class. It gets all the data to handle the build (such as {@LinkDefaultProductFlavor}, {@link DefaultBuildType}, and dependencies)
- Create DataBindingBuilder
- Forces the use of at least the minimum version of the currently supported plug-in
- Applying Java plug-ins
- Create build cache. If enabled, the build cache directory is set to the user-defined directory, or the default directory if the user-defined directory is not provided
- The global scope is created to save data for the Android Plugin
- Go to assemble Task and add the description
- Listen for build completion callbacks and do some cleanup
ConfigureExtension configureExtension
What is the extension that I won’t repeat, read my previous blog to go
private void configureExtension() {
// If this looks familiar, yes this is the config variable number of extensions, this is the config BuildType,
// This is why we can customize the name in BuildType
final NamedDomainObjectContainer<BuildType> buildTypeContainer =
project.container(
BuildType.class.new BuildTypeFactory(
objectFactory,
project,
extraModelInfo.getSyncIssueHandler(),
extraModelInfo.getDeprecationReporter()));
// Use the same method to extend ProductFlavor
final NamedDomainObjectContainer<ProductFlavor> productFlavorContainer =
project.container(
ProductFlavor.class.new ProductFlavorFactory(
objectFactory,
project,
extraModelInfo.getDeprecationReporter(),
project.getLogger()));
// The same is true for the indefinite item extension SigningConfig
final NamedDomainObjectContainer<SigningConfig> signingConfigContainer =
project.container(
SigningConfig.class.new SigningConfigFactory(
objectFactory,
GradleKeystoreHelper.getDefaultDebugKeystoreLocation()));
// The same is true for the indefinite extension BaseVariantOutput
final NamedDomainObjectContainer<BaseVariantOutput> buildOutputs =
project.container(BaseVariantOutput.class);
project.getExtensions().add("buildOutputs", buildOutputs);
sourceSetManager =
new SourceSetManager(
project,
isPackagePublished(),
globalScope.getDslScope(),
new DelayedActionsExecutor());
// This is to create the Android extension
extension =
createExtension(
project,
projectOptions,
globalScope,
sdkHandler,
buildTypeContainer,
productFlavorContainer,
signingConfigContainer,
buildOutputs,
sourceSetManager,
extraModelInfo);
globalScope.setExtension(extension);
variantFactory = createVariantFactory(globalScope, extension);
// Create TaskManager, task management class
taskManager =
createTaskManager(
globalScope,
project,
projectOptions,
dataBindingBuilder,
extension,
sdkHandler,
variantFactory,
registry,
threadRecorder);
// Create the VariantManager class
variantManager =
newVariantManager( globalScope, project, projectOptions, extension, variantFactory, taskManager, sourceSetManager, threadRecorder); registerModels(registry, globalScope, variantManager, extension, extraModelInfo); .// Create DSLS of signingConfig Debug, buildType Debug, and buildType Releas in sequence.
// create default Objects, signingConfig first as its used by the BuildTypes.
variantFactory.createDefaultComponents(
buildTypeContainer, productFlavorContainer, signingConfigContainer);
}
Copy the code
This method mainly does a few things
-
Configure variable extension BuildType, ProductFlavor, SigningConfig, BaseVariantOutput
buildTypes { release {} debug {} vip {} aaa {} } flavorDimensions "xiaomi"."huawei" productFlavors { JD { dimension("xiaomi") } TAO { dimension("xiaomi") } PIN { dimension("huawei")}}Copy the code
So we can customize the name of each item in this way, you can configure multiple items
-
Create android extensions, which we use most often
android { compileSdkVersion 29. }Copy the code
-
Create TaskManager, task management class
-
Create the VariantManager, the variant management class
-
Create DSLS of signingConfig Debug, buildType Debug, and buildType Releas in sequence.
createTasks
private void createTasks() {
threadRecorder.record(
ExecutionType.TASK_MANAGER_CREATE_TASKS,
project.getPath(),
null,
() -> taskManager.createTasksBeforeEvaluate());
project.afterEvaluate(
CrashReporting.afterEvaluate(
p -> {
sourceSetManager.runBuildableArtifactsActions();
threadRecorder.record(
ExecutionType.BASE_PLUGIN_CREATE_ANDROID_TASKS,
project.getPath(),
null.this::createAndroidTasks);
}));
}
Copy the code
This method is divided into two phases, the first is to create createTasks after BeforeEvaluate() and the second is to createAndroidTasks after afterEvaluate
AndroidTask is created after afterEvaluate because it relies on configuration items to generate tasks. BeforeEvaluate creates tasks that have little to do with compilation. CreateAndroidTasks after afterEvaluate
createAndroidTasks
final void createAndroidTasks() {
...
// Create an application Task
List<VariantScope> variantScopes = variantManager.createAndroidTasks();
}
Copy the code
Next, let’s look at createAndroidTasks for variantManager
public List<VariantScope> createAndroidTasks() {
variantFactory.validateModel(this);
variantFactory.preVariantWork(project);
// Check whether the list is null, and if it is, fill in the list
if (variantScopes.isEmpty()) {
populateVariantDataList();
}
// Create top level test tasks.
// Create a project level test tasktaskManager.createTopLevelTestTasks(! productFlavors.isEmpty());// Run the variantScopes and create a Task for each VariantData
for (final VariantScope variantScope : variantScopes) {
createTasksForVariantData(variantScope);
}
// Create a task related to the report
taskManager.createSourceSetArtifactReportTask(globalScope);
taskManager.createReportTasks(variantScopes);
return variantScopes;
}
Copy the code
For main see createTasksForVariantData method
public void createTasksForVariantData(final VariantScope variantScope) {
final BaseVariantData variantData = variantScope.getVariantData();
final VariantType variantType = variantData.getType();
final GradleVariantConfiguration variantConfig = variantScope.getVariantConfiguration();
// Create Assemble Task
taskManager.createAssembleTask(variantData);
if (variantType.isBaseModule()) {
taskManager.createBundleTask(variantData);
}
if (variantType.isTestComponent()) {
...
} else {
/ / if it is not a Test moudle, will call ApplicationTaskManager createTasksForVariantScope method.taskManager.createTasksForVariantScope(variantScope); }}Copy the code
This will eventually call ApplicationTaskManager createTasksForVariantScope, within this method to create a series of Task is suitable for the application to build
@Override
public void createTasksForVariantScope(@NonNull final VariantScope variantScope) {
createAnchorTasks(variantScope);
createCheckManifestTask(variantScope);
handleMicroApp(variantScope);
// Create all current streams (dependencies mostly at this point)
createDependencyStreams(variantScope);
// Add a task to publish the applicationId.
createApplicationIdWriterTask(variantScope);
// Add a task to process the manifest(s)
// Add a task to create the res values
// Add a task to compile renderscript files.
// Add a task to merge the resource folders
// Add tasks to compile shader
// Add a task to merge the asset folders
// Add a task to create the BuildConfig class
// Add a task to process the Android Resources and generate source files
// Add a task to process the java resources
// Add external native build tasks
// Add a task to merge the jni libs folders
// Add feature related tasks if necessary
// Add data binding tasks if enabled
// Add a compile task
}
Copy the code
The code is so long that only comments are left, and the comments also make it clear that there is a list of tasks generated for compileXXX, generateXXX, processXXX, mergeXXX, which are the occasional tasks needed to build a fully running APK
For some basic knowledge about Task, please refer to my Gradle Learning series (2) : Gradle Core Exploration
How do I view the implementation class for each Task?
Let’s take this for example
createCheckManifestTask(variantScope);
Copy the code
Let’s follow the source code to have a look
public void createCheckManifestTask(@NonNull VariantScope scope) {
taskFactory.register(getCheckManifestConfig(scope));
}
Copy the code
TaskFactoryImpl register TaskFactoryImpl register TaskFactoryImpl register
override fun <T : Task> register(creationAction: TaskCreationAction<T>): TaskProvider<T> =
taskContainer.registerTask(creationAction, null.null.null)
Copy the code
Continue to follow up the source code
fun <T : Task> TaskContainer.registerTask(
creationAction: TaskCreationAction<T>,
secondaryPreConfigAction: PreConfigAction? = null,
secondaryAction: TaskConfigAction<in T>? = null,
secondaryProviderCallback: TaskProviderCallback<T>? = null
): TaskProvider<T> {
val actionWrapper = TaskAction(creationAction, secondaryPreConfigAction, secondaryAction, secondaryProviderCallback)
return this.register(creationAction.name, creationAction.type, actionWrapper)
.also { provider ->
actionWrapper.postRegisterHook(provider)
}
}
Copy the code
TaskContainer register(String VAR1, Class
var2, Action
var3);
So ultimately it’s up to the Gradle API documentation to see what this method does
You create a new task, and the task name and type are the parameters name and type
So you need to go back and see what are the names and types that were passed in
public void createCheckManifestTask(@NonNull VariantScope scope) {
taskFactory.register(getCheckManifestConfig(scope));
}
protected CheckManifest.CreationAction getCheckManifestConfig(@NonNull VariantScope scope) {
return new CheckManifest.CreationAction(scope, false);
}
Copy the code
Continue to look at the new CheckManifest CreationAction
public static class CreationAction extends VariantTaskCreationAction<CheckManifest> {.@NonNull
@Override
public String getName() {
return getVariantScope().getTaskName("check"."Manifest");
}
@NonNull
@Override
public Class<CheckManifest> getType() {
return CheckManifest.class; }... }Copy the code
The name of the Task is checkXXXManifest, and the type of the Task is CheckManifest
public class CheckManifest extends AndroidVariantTask {. }abstract class AndroidVariantTask : DefaultTask(), VariantAwareTask {
}
Copy the code
This finds a Task implementation class
Dex compilation process source analysis
We start with this method Add a compile task
@Override
public void createTasksForVariantScope(@NonNull finalVariantScope variantScope) { createAnchorTasks(variantScope); createCheckManifestTask(variantScope); .// Add a compile taskcreateCompileTask(variantScope); . }Copy the code
protected void createCompileTask(@NonNull VariantScope variantScope) {
TaskProvider<? extends JavaCompile> javacTask = createJavacTask(variantScope);
addJavacClassesStream(variantScope);
setJavaCompilerTask(javacTask, variantScope);
createPostCompilationTasks(variantScope);
}
Copy the code
First, we create a Javac Task called compileXXXJavaWithJavac with the implementation class AndroidJavaCompile. The Task is to compile Java source files into classes. Look at the next createPostCompilationTasks method
public void createPostCompilationTasks(
@NonNull final VariantScope variantScope) {
AndroidConfig extension = variantScope.getGlobalScope().getExtension();
// Merge Java Resources.
// Merge Java resources
createMergeJavaResTransform(variantScope);
// ----- External Transforms -----
// apply all the external transforms.
// Add all transforms to the transformManager
List<Transform> customTransforms = extension.getTransforms();
List<List<Object>> customTransformsDependencies = extension.getTransformsDependencies();
for (int i = 0, count = customTransforms.size(); i < count; i++) {
Transform transform = customTransforms.get(i);
List<Object> deps = customTransformsDependencies.get(i);
transformManager.addTransform(
taskFactory,
variantScope,
transform,
null,
task -> {
if(! deps.isEmpty()) { task.dependsOn(deps); } }, taskProvider -> {// if the task is a no-op then we make assemble task depend on it.
if(transform.getScopes().isEmpty()) { TaskFactoryUtils.dependsOn( variantScope.getTaskContainer().getAssembleTask(), taskProvider); }}); }...// ----- Minify next -----
// If minifyEnabled=true, code reduction is obfuscated
CodeShrinker shrinker = maybeCreateJavaCodeShrinkerTransform(variantScope);
if (shrinker == CodeShrinker.R8) {
// Perform resource reduction for R8
maybeCreateResourcesShrinkerTransform(variantScope);
maybeCreateDexSplitterTransform(variantScope);
// TODO: create JavaResSplitterTransform and call it here (http://b/77546738)
return;
}
// ----- 10x support
/ / InstantRun support
TaskProvider<PreColdSwapTask> preColdSwapTask = null;
if (variantScope.getInstantRunBuildContext().isInInstantRunMode()) {
TaskProvider<? extends Task> allActionsAnchorTask =
createInstantRunAllActionsTasks(variantScope);
assertvariantScope.getInstantRunTaskManager() ! =null;
preColdSwapTask =
variantScope.getInstantRunTaskManager().createPreColdswapTask(projectOptions);
TaskFactoryUtils.dependsOn(preColdSwapTask, allActionsAnchorTask);
}
// ----- Multi-Dex support
/ / Multi - Dex support
DexingType dexingType = variantScope.getDexingType();
// Upgrade from legacy multi-dex to native multi-dex if possible when using with a device
if (dexingType == DexingType.LEGACY_MULTIDEX) {
if (variantScope.getVariantConfiguration().isMultiDexEnabled()
&& variantScope
.getVariantConfiguration()
.getMinSdkVersionWithTargetDeviceApi()
.getFeatureLevel()
>= 21) { dexingType = DexingType.NATIVE_MULTIDEX; }}...// Create a Dex taskcreateDexTasks(variantScope, dexingType); . }Copy the code
This is mainly done a few work
- Java Resource merging
- Adds all transforms to the transformManager
- If minifyEnabled=true, code reduction, obfuscation
- For R8, perform resource reduction
- InstantRun support
- Multi – Dex support
- Create Dex task
The Dex Task created here is actually a TransfromTask type. Let’s learn more about TransfromTask
/** A task running a transform. */
@CacheableTask
public class TransformTask extends StreamBasedTask implements Context {
@TaskAction
void transform(final IncrementalTaskInputs incrementalTaskInputs)
throws IOException, TransformException, InterruptedException {
....
recorder.record(
ExecutionType.TASK_TRANSFORM,
executionInfo,
getProject().getPath(),
getVariantName(),
new Recorder.Block<Void>() {
@Override
public Void call() throws Exception {
transform.transform(
new TransformInvocationBuilder(TransformTask.this) .addInputs(consumedInputs.getValue()) .addReferencedInputs(referencedInputs.getValue()) .addSecondaryInputs(changedSecondaryInputs.getValue()) .addOutputProvider( outputStream ! =null
? outputStream.asOutput(
isIncremental.getValue())
: null)
.setIncrementalMode(isIncremental.getValue())
.build());
if(outputStream ! =null) {
outputStream.save();
}
return null; }}); }... }Copy the code
Finally call the transform of the transform, that is, call the DexArchiveBuilderTransform transform
public class DexArchiveBuilderTransform extends Transform {. }Copy the code
For more information about Transform, see my previous article Gradle Learning Series (5) : Gradle Transform
Reading this, I suddenly realized that we had customizedTransform
, added to theextension
, and then compile during the packaging phasejava->class
After that,class->dex
Before, iterateextension
In theTransform
(including our custom ones) and then add toTransformManager
Registered as aTransformTask
And when you performTask
Time, in the callTransform
thetransform
Method, so that we get to thedex
The purpose of the previous modification of bytecode
Next, continue to see DexArchiveBuilderTransform transform method
@Override
public void transform(@NonNull TransformInvocation transformInvocation)
throws TransformException, IOException, InterruptedException {
// This is to iterate over the class files in the directory
for (TransformInput input : transformInvocation.getInputs()) {
for (DirectoryInput dirInput : input.getDirectoryInputs()) {
logger.verbose("Dir input %s", dirInput.getFile().toString());
convertToDexArchive(
transformInvocation.getContext(),
dirInput,
outputProvider,
isIncremental,
bootclasspathServiceKey,
classpathServiceKey,
additionalPaths);
}
// The class file in the jar is traversed
for (JarInput jarInput : input.getJarInputs()) {
logger.verbose("Jar input %s", jarInput.getFile().toString());
D8DesugaringCacheInfo cacheInfo =
getD8DesugaringCacheInfo(
desugarIncrementalTransformHelper,
bootclasspath,
classpath,
jarInput);
List<File> dexArchives =
processJarInput(
transformInvocation.getContext(),
isIncremental,
jarInput,
outputProvider,
bootclasspathServiceKey,
classpathServiceKey,
additionalPaths,
cacheInfo);
if(cacheInfo ! = D8DesugaringCacheInfo.DONT_CACHE && ! dexArchives.isEmpty()) { cacheableItems.add(newDexArchiveBuilderCacheHandler.CacheableItem( jarInput, dexArchives, cacheInfo.orderedD8DesugaringDependencies)); }}}}Copy the code
Let’s continue with the convertToDexArchive method, which, as you can see from its name, converts to a dex file
private List<File> convertToDexArchive(
@NonNull Context context,
@NonNull QualifiedContent input,
@NonNull TransformOutputProvider outputProvider,
boolean isIncremental,
@NonNull ClasspathServiceKey bootClasspath,
@NonNull ClasspathServiceKey classpath,
@NonNull Set<File> additionalPaths) {... executor.execute( ...try(Closeable ignored = output = outputHandler.createOutput()) { launchProcessing( parameters, output.getStandardOutput(), output.getErrorOutput(), messageReceiver); }}}return dexArchives.build();
}
Copy the code
Finally, the launchProcessing method is called to follow up the source code
private static void launchProcessing(
@NonNull DexConversionParameters dexConversionParameters,
@NonNull OutputStream outStream,
@NonNull OutputStream errStream,
@NonNull MessageReceiver receiver)
throws IOException, URISyntaxException { DexArchiveBuilder dexArchiveBuilder = getDexArchiveBuilder( dexConversionParameters.minSdkVersion, dexConversionParameters.dexAdditionalParameters, dexConversionParameters.inBufferSize, dexConversionParameters.outBufferSize, dexConversionParameters.bootClasspath, dexConversionParameters.classpath, dexConversionParameters.dexer, dexConversionParameters.isDebuggable, VariantScope.Java8LangSupport.D8 == dexConversionParameters.java8LangSupportType, outStream, errStream, receiver); .try (ClassFileInput input = ClassFileInputs.fromPath(inputPath);
Stream<ClassFileEntry> entries = input.entries(bucketFilter)) {
dexArchiveBuilder.convert(
entries,
Paths.get(new URI(dexConversionParameters.output)),
dexConversionParameters.isDirectoryBased());
} catch (DexArchiveBuilderException ex) {
throw new DexArchiveBuilderException("Failed to process "+ inputPath.toString(), ex); }}Copy the code
Final call dexArchiveBuilder. Convert and dexArchiveBuilder will have two implementation class D8DexArchiveBuilder and DxDexArchiveBuilder converting here in the dex file
Dex Compilation Summary
- The first step is to create a JavAC Task whose Task is to compile Java source files into classes
- Java Resource merging
- Adds all transforms to the transformManager
- If minifyEnabled=true, code reduction, obfuscation
- For R8, perform resource reduction
- InstantRun support
- Multi – Dex support
- Create Dex task
- In the end
D8DexArchiveBuilder
和DxDexArchiveBuilder
In this case, convert to dex file
conclusion
The Android Gradle Plugin does three main things
-
Configuration project, mainly do the following work
- Create SdkHandler, an SDK that handles everything for the Gradle plug-in. There is about one instance per project
- Create AndroidBuilder, which is the main builder class. It gets all the data to handle the build (such as {@LinkDefaultProductFlavor}, {@link DefaultBuildType}, and dependencies)
- Create DataBindingBuilder
- Forces the use of at least the minimum version of the currently supported plug-in
- Applying Java plug-ins
- Create build cache. If enabled, the build cache directory is set to the user-defined directory, or the default directory if the user-defined directory is not provided
- The global scope is created to save data for the Android Plugin
- Go to assemble Task and add the description
- Listen for build completion callbacks and do some cleanup
-
Configuration extensions
-
Configure variable extension BuildType, ProductFlavor, SigningConfig, BaseVariantOutput
buildTypes { release {} debug {} vip {} aaa {} } flavorDimensions "xiaomi"."huawei" productFlavors { JD { dimension("xiaomi") } TAO { dimension("xiaomi") } PIN { dimension("huawei")}}Copy the code
So we can customize the name of each item in this way, you can configure multiple items
-
Create android extensions, which we use most often
android { compileSdkVersion 29. }Copy the code
-
Create TaskManager, task management class
-
Create the VariantManager, the variant management class
-
Create DSLS of signingConfig Debug, buildType Debug, and buildType Releas in sequence.
-
-
Create a task
This will eventually call ApplicationTaskManager createTasksForVariantScope, create a series of within this method is suitable for application to build a Task, only keep the comments here
@Override public void createTasksForVariantScope(@NonNull final VariantScope variantScope) { createAnchorTasks(variantScope); createCheckManifestTask(variantScope); handleMicroApp(variantScope); // Create all current streams (dependencies mostly at this point) createDependencyStreams(variantScope); // Add a task to publish the applicationId. createApplicationIdWriterTask(variantScope); // Add a task to process the manifest(s) // Add a task to create the res values // Add a task to compile renderscript files. // Add a task to merge the resource folders // Add tasks to compile shader // Add a task to merge the asset folders // Add a task to create the BuildConfig class // Add a task to process the Android Resources and generate source files // Add a task to process the java resources // Add external native build tasks // Add a task to merge the jni libs folders // Add feature related tasks if necessary // Add data binding tasks if enabled // Add a compile task } Copy the code
-
Then I analyzed how to find the implementation class of each Task
-
Finally, the workflow of Dex Task is analyzed
reference
【 soul 7 ask 】 deep exploration Gradle automatic building technology (five, Gradle plug-in architecture implementation principle analysis — next)
Android Gradle Plugin source code analysis
🍵 Complete the Android skill tree — from the AGP build process to the APK packaging process