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!