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 , and AppPlugin inherits from AbstractAppPlugin, so keep looking

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 , so this is the final implementation class, so let’s take a look at apply, which is the main implementation, which calls the basePluginApply(Project) method, so let’s take a look at the source code

 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->classAfter that,class->dexBefore, iterateextensionIn theTransform(including our custom ones) and then add toTransformManagerRegistered as aTransformTaskAnd when you performTaskTime, in the callTransformthetransformMethod, so that we get to thedexThe 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 endD8DexArchiveBuilderDxDexArchiveBuilderIn 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