1. Basic preparation

Before analyzing the source code, YOU should have a basic understanding of the Android packaging process, at least the following packaging process:

Otherwise, you may not be aware of the technical terms listed below.

2. Open AGP source code

When I was looking at THE AGP code, I was struggling with whether to download the source code of AGP. Later, I listened to my colleague’s advice and directly used the code that the project relied on for analysis.

There are two main reasons:

1. AGP source code is too big, 30GB, and the version is very old.

2. Using AGP code that your project depends on is simple.

Just add it to the project

Implementation "com. Android. Tools. Build: gradle: 4.4.1." "Copy the code

Can view.


3. Code analysis

By the way, the AGP version is 4.1.1.

The first step is to find the AppPlugin

In AS, if a project is created, it is added under the main module by default:

apply plugin: 'com.android.application'  

Copy the code

Custom Plugin’s friends all know that the source code. There must be a com in android. Application. The properties file and at the same time, this is we the entrance to the Plugin.

Global search com. Android. Application, open the com. Android. Application. The properties, content is:

implementation-class=com.android.build.gradle.AppPlugin  

Copy the code

Click the “Command” button to click the source code, find a Plugin declared in the AppPlugin, and finally jump to:

implementation-class=com.android.build.gradle.internal.plugins.AppPlugin
Copy the code

The package name is different from the previous one, and this is our final entrance.

Do you have any doubts? I added the apply plugin: com.android. Application. When will this code be called?

If you’ve noticed, every time we make a change to our build.gradle file, AS tells us to click on the “Sync Now” button, which triggers the configuration process in gradle, and finally runs Plugin#apply. You can verify this when you customize your Plugin.

The second step AppPlugin

AbstractAppPlugin > AbstractAppPlugin > AbstractAppPlugin > AbstractAppPlugin > AbstractAppPlugin > AbstractAppPlugin > AbstractAppPlugin > AbstractAppPlugin > AbstractAppPlugin > AbstractAppPlugin > AbstractAppPlugin > AbstractAppPlugin > AbstractAppPlugin > AbstractAppPlugin < AbstractAppPlugin >

@Override  
public final void apply(@NonNull Project project) {  
    CrashReporting.runAction(  
            () -> {  
                basePluginApply(project);  
                pluginSpecificApply(project);  
            });  
}  

Copy the code

Here we only need to focus on the two methods in the method block, basePluginApply and pluginSpecificApply.

BasePluginApply () {basePluginApply () {basePluginApply ();

private void basePluginApply(@NonNull Project project) { // ... Code to check the DependencyResolutionChecks omitted / / dependence. RegisterDependencyCheck (project, projectOptions); / /... Omit the path to check, check module, build parameters, such as the listener / / AGP version check AgpVersionChecker enforceTheSamePluginVersions (project); RecordingBuildListener buildListener = profilerInitializer.init (project, projectOptions); ProfileAgent.INSTANCE.register(project.getName(), buildListener); threadRecorder = ThreadRecorder.get(); / /... ThreadRecorder. Record (executionType.base_plugin_project_configure, project.getPath(), null, this::configureProject); ThreadRecorder. Record (executionType.base_plugin_project_base_extension_creation, project.getPath(), null, this::configureExtension); // create Task threadRecorder. Record (executionType.base_plugin_project_tasks_creation, project.getPath(), null, this::createTasks); }Copy the code

The key methods I have highlighted are configuring projects, configuring extensions, and creating tasks.

Step 3 Configure Project

It is important to note that this configuration is not for the Gradle lifecycle, but for the current Project.

private void configureProject() { // ... Perform a large number of Service / / depend on the version of the relevant Provider < ConstraintHandler. CachedStringBuildService > cachedStringBuildServiceProvider = new ConstraintHandler.CachedStringBuildService.RegistrationAction(project) .execute(); / / maven cache Provider < MavenCoordinatesCacheBuildService > MavenCoordinatesCacheBuildService = new MavenCoordinatesCacheBuildService.RegistrationAction( project, cachedStringBuildServiceProvider) .execute(); / / dependent libraries related new LibraryDependencyCacheBuildService RegistrationAction (project). The execute (); . / / aapt preparations for the new Aapt2WorkersBuildService RegistrationAction (project, projectOptions). The execute (); new Aapt2DaemonBuildService.RegistrationAction(project).execute(); new SyncIssueReporterImpl.GlobalSyncIssueService.RegistrationAction( project, SyncOptions.getModelQueryMode(projectOptions)) .execute(); / / SDK Provider < SdkComponentsBuildService > SdkComponentsBuildService = new SdkComponentsBuildService.RegistrationAction( project, projectOptions, project.getProviders() .provider(() -> extension.getCompileSdkVersion()), project.getProviders() .provider(() -> extension.getBuildToolsRevision()), project.getProviders().provider(() -> extension.getNdkVersion()), project.getProviders().provider(() -> extension.getNdkPath())) .execute(); // Enforce minimum versions of certain plugins GradlePluginUtils.enforceMinimumVersionsOfPlugins(project, issueReporter); // Apply the Java plugin project.getPlugins().apply(JavaBasePlugin.class); dslServices = new DslServicesImpl( projectServices, new DslVariableFactory(syncIssueReporter), sdkComponentsBuildService); MessageReceiverImpl messageReceiver = new MessageReceiverImpl SyncOptions.getErrorFormatMode(projectOptions), projectServices.getLogger()); / /... Omit createLintClasspathConfiguration (project); }Copy the code

My understanding of the above code is that it is preparation for creating a Task, and the xxxactions described in the above code are also confusing and do not correspond to actions in a Task.

Step 4 Confirm the extension

The method to verify that the extension corresponds to is configureExtension.

The build.gradle file in your app module usually has something like this:

android { compileSdk 32 defaultConfig { applicationId "com.qidian.test" minSdk 21 targetSdk 32 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx. Test. Runner. AndroidJUnitRunner"} buildTypes {release {minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } debug { minifyEnabled false } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 TargetCompatibility JavAns.version_1_8} kotlinOptions {jvmTarget = '1.8'}}Copy the code

The purpose of configureExtension is to convert such script information into information that code can recognize:

Private void configureExtension () {/ / Gradle DSL help DslServices DslServices = globalScope. GetDslServices (); final NamedDomainObjectContainer<BaseVariantOutput> buildOutputs = project.container(BaseVariantOutput.class); / /... //... VariantFactory = createVariantFactory(projectServices, globalScope); variantFactory = createVariantFactory(projectServices, globalScope); variantInputModel = new LegacyVariantInputManager( dslServices, variantFactory.getVariantType(), new SourceSetManager( project, isPackagePublished(), dslServices, new DelayedActionsExecutor())); // createExtension extension = createExtension(dslServices, globalScope, variantInputModel, buildOutputs, extraModelInfo); globalScope.setExtension(extension); variantManager = new VariantManager<>( globalScope, project, projectServices.getProjectOptions(), extension, variantFactory, variantInputModel, projectServices, threadRecorder); registerModels( registry, globalScope, variantInputModel, extension, extraModelInfo); // create default Objects, signingConfig first as its used by the BuildTypes. variantFactory.createDefaultComponents(variantInputModel); / /... }Copy the code

A brief look at the code shows that most of the code is related to variant and extension.

Taking a closer look at the generated extension, BasePlugin#createExtension is an abstract method that was eventually passed to the AppPlugin#createExtension method:

protected AppExtension createExtension(  
        @NonNull DslServices dslServices,  
        @NonNull GlobalScope globalScope,  
        @NonNull  
                DslContainerProvider<DefaultConfig, BuildType, ProductFlavor, SigningConfig>  
                dslContainers,  
        @NonNull NamedDomainObjectContainer<BaseVariantOutput> buildOutputs,  
        @NonNull ExtraModelInfo extraModelInfo) {  
    return project.getExtensions()  
            .create(  
                    "android",  
                    getExtensionClass(),  
                    dslServices,  
                    globalScope,  
                    buildOutputs,  
                    dslContainers.getSourceSetManager(),  
                    extraModelInfo,  
                    new ApplicationExtensionImpl(dslServices, dslContainers));  
}  

Copy the code

This may seem unfamiliar at first, but if you’ve ever developed a plugin, you’ll know AppExtension, which gets any information from android {} under build.gradle mentioned above.

Step 5 Create Task

The BasePlugin#createTasks method is used to create the Task:

Private void createTasks() {// Register a task that is not Variant related threadRecorder. Record (ExecutionType.TASK_MANAGER_CREATE_TASKS, project.getPath(), null, () -> TaskManager.createTasksBeforeEvaluate( globalScope, variantFactory.getVariantType(), extension.getSourceSets())); // After the Gradle configuration phase is complete, Registered with the Variant related task project. AfterEvaluate (CrashReporting. AfterEvaluate (p - > { variantInputModel.getSourceSetManager().runBuildableArtifactsActions(); threadRecorder.record( ExecutionType.BASE_PLUGIN_CREATE_ANDROID_TASKS, project.getPath(), null, this::createAndroidTasks); })); }Copy the code

There are two main methods in this method:

1. TaskManager# createTasksBeforeEvaluate: static methods in the Project configuration, will create a Task.

2. CreateAndroidTasks: A callback is registered after the configuration life cycle is completed.

TaskManager# createTasksBeforeEvaluate is a large section of the registered Task code inside, interested can look up the source code.

Step 6 Create a Task after the configuration

Wait for Project to enter the configuration lifecycle callback and go to createAndroidTasks:

final void createAndroidTasks() { if (extension.getCompileSdkVersion() == null) { // ... CompileSdkVersion related} //... // get current plugins and look for the default Java plugin. if (project.getPlugins().hasPlugin(JavaPlugin.class)) { throw new BadPluginException( "The 'java' plugin has been applied, but it is not compatible with the Android plugins.");  } / /... / / set a few configuration ProcessProfileWriter. GetProject (project. GetPath ()). SetCompileSdk (extension) getCompileSdkVersion ()) .setBuildToolsVersion(extension.getBuildToolsRevision().toString()) .setSplits(AnalyticsUtil.toProto(extension.getSplits())); String kotlinPluginVersion = getKotlinPluginVersion(); if (kotlinPluginVersion ! = null) { ProcessProfileWriter.getProject(project.getPath()) .setKotlinPluginVersion(kotlinPluginVersion); } AnalyticsUtil.recordFirebasePerformancePluginVersion(project); A / / comment to create the Variant variantManager. CreateVariants (); List<ComponentInfo<VariantT, VariantPropertiesT>> variants = variantManager.getMainComponents(); TaskManager<VariantT, VariantPropertiesT> taskManager = createTaskManager( variants, variantManager.getTestComponents(), ! variantInputModel.getProductFlavors().isEmpty(), globalScope, extension, threadRecorder); Create Task taskManager.createTasks(); / /... / / comment three created Task configure compose related tasks. The taskManager. CreatePostApiTasks (); // now publish all variant artifacts for non test variants since // tests don't publish anything. for (ComponentInfo<VariantT, VariantPropertiesT> component : variants) { component.getProperties().publishBuildArtifacts(); } / /... variantManager.setHasCreatedTasks(true); // notify our properties that configuration is over for us. GradleProperty.Companion.endOfEvaluation(); }Copy the code

First, you can see from comment 1 that all variants have been created in this step.

Then, from comments 2 and 3, we can see that createAndroidTasks creates tasks using taskManager twice.

** Step 7 The TaskManager creates multiple tasks for the first time

**

The TaskManager#createTasks method used to create the Task for the first time:

Public void createTasks() {// Lint-related Task TaskFactory.register (new) PrepareLintJarForPublish.CreationAction(globalScope)); // create a lifecycle task to build the lintChecks dependencies taskFactory.register( COMPILE_LINT_CHECKS_TASK, task -> task.dependsOn(globalScope.getLocalCustomLintChecks())); // Create top level test tasks. createTopLevelTestTasks(); / / key, Variants (variants and tests) for (ComponentInfo<VariantT, variantproperties > Variant: variants) { createTasksForVariant(variant, variants); } // Test related Task for (ComponentInfo< testEntimpl <? extends TestComponentPropertiesImpl>, TestComponentPropertiesImpl> testComponent : testComponents) { createTasksForTest(testComponent); } // Task createReportTasks(); }Copy the code

There are still many tasks registered, such as Lint, testing and logging related tasks.

The most important method is to obtain the variants created above, and iterate through the createTasksForVariant method. Let’s see what methods it registers for each Variant:

private void createTasksForVariant( @NonNull ComponentInfo<VariantT, VariantPropertiesT> variant, @NonNull List<ComponentInfo<VariantT, VariantPropertiesT>> variants) { // ... Omit createAssembleTask (variantProperties); if (variantType.isBaseModule()) { createBundleTask(variantProperties); } doCreateTasksForVariant(variant, variants); }Copy the code

The createAssembleTask method, which you’ll be familiar with because we use commands like assembleDebug or assembleRelease every time we package, creates the AssembleTask.

The doCreateTasksForVariant method is a method for creating variation-related tasks, but in TaskManager it is an abstract method that is implemented by ApplicationTaskManager.

So what tasks are created in it? Scroll back and the pictures will tell you!

Step 8 TaskManager creates multiple tasks for the second time

TaskManager#createPostApiTasks (ViewBinding, DataBinding, Kotlin);

Here is not a and students analysis, directly look at the picture:

The Task process

Here’s a quick explanation:

Blue: Gradle createTasksBeforeEvaluate registered Task before configuration phase.

Orange: Tasks created after the Gradle configuration phase is complete.

Red: Important Task.

Arrow: Dependencies (not all).

Of course, I don’t list all tasks, and dependencies only list what I see (too much code, not all read).

If we combine the above picture with the previous official packaging flow chart, we find that many of them can be matched:

1. AIDL, Source Code, Resource files, etc.

2. Mid-term tasks related to Class compilation and code obfuscation.

3. In the later stage, Dex was created and merged, and APK-related tasks were packaged.

Also, there are dependencies between tasks (not shown in the figure), such as when I run the following command:

./gradlew assembleDebug  

Copy the code

This command will call the assembleDebug Task, after which it will finish executing the previously dependent tasks such as resource handling, compile related, packaging to generate the APK we want, and so on.

At this point, the source code analysis is almost complete, back to the second step, BasePlugin in the apply method, also executes the pluginSpecificApply method, but this method is an empty method.


3. Summary

The purpose of this article is to give you an outline of AGP. What does AGP mainly do?

It can be found that most tasks registered in AGP are for packaging services, and each small Task is a screw to package the assembly line.

If you think this article is good, “like” is the best affirmation!