总听说AGP,它到底做了什么?

·  阅读 2980
总听说AGP,它到底做了什么?

前言

故事的开始是这样的。

之前阅读《Android开发高手课》的时候,里面启动优化一栏有讲到 systrace + 函数插桩 是不错的卡顿排查方式。

主要方式就是通过 Transform + Asm,相信是大家的老熟人了。

使用其中的 Demo 进行学习的时候,发现将 AGP(Android Gradle Plugin,Android Gradle 打包插件) 升级到 4.0.0 以后,Demo 就不管用了。

分析了一下 Demo,发现代码中没有使用直接注册 Transform 的方式进行插桩,而是获取 transformClassesWithDexBuilderForxxx 对应的 Task,通过反射的方式将该 Task 中的 Transform 设置为当前实现的 Transform:

Transform.png

那为什么在 AGP 4.0.0 的时候,这种方式就不行了呢?

我们都知道,AS 在构建代码的过程中会将执行的 Task 都打印到 Build 窗口中,通过观察不同版本的 AGP 的构建过程,发现 4.0.0 的构建过程中没有打印 transformClassesWithDexBuilderForxxx

啊,这...

让人头秃

一个好端端的 Task 说没了就没了,好吧,AGP 源码见!

这边文章先和大家简单分析一下 AGP 的作用,后续的文章再和大家分析 Transform 的源码。

一、基础准备

在分析源码之前,我想你应该对 Android 打包流程已经有基础的了解,至少了解了下图的打包过程:

image.png

否则你有可能不了解下文中的专业术语。

二、AGP源码的打开方式

看 AGP 代码的时候,我一直纠结要不要下载 AGP 的源码,后来听同事大佬建议,直接使用了项目依赖的代码进行分析。

主要的原因有两点:

  1. AGP 的源码太大了,有30g,并且版本已经很旧了
  2. 使用项目依赖的 AGP 代码很简单

只要在项目中加入

implementation "com.android.tools.build:gradle:4.1.1"
复制代码

即可查看。

三、代码分析

顺便说一下,AGP 的版本是 4.1.1。

第一步 寻找AppPlugin

在 AS 中,如果创建了一个项目,默认在主模块下面添加:

apply plugin: 'com.android.application'
复制代码

自定义过 Plugin 的小伙伴都知道,源码中一定有一个 com.android.application.properties 文件与之相对应,这便是我们 Plugin 的入口了。

全局搜 com.android.application,打开 com.android.application.properties,内容是:

implementation-class=com.android.build.gradle.AppPlugin
复制代码

按「Command」按钮点击源码,发现 AppPlugin 里面又声明了一个 Plugin,最终跳到了:

implementation-class=com.android.build.gradle.internal.plugins.AppPlugin
复制代码

包名与之前的不一样,这才是我们的最终入口。

各位同学有没有这样的疑惑,我给加上 apply plugin: com.android.application,那这段代码什么时候调用呢?

不知道大家有没有注意到,每次改动 build.gradle 文件的时候,AS 都会让我们点击 「Sync Now」按钮,点击完了,就会触发 Gradle 中的配置过程,最终会运行 Plugin#apply 方法,大家可以自定义 Plugin 的时候验证一下。

第二步 AppPlugin

AppPlugin 的父类是 AbstractAppPluginAbstractAppPlugin 的父类是 BasePlugin,插件的开始就在 BasePlugin#apply 方法里面:

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

这里我们只需要关注方法块里面的两个方法 basePluginApplypluginSpecificApply

进入重点方法 basePluginApply 方法,这个方法的前期做了很多的检查工作,包括路径、版本和 AGP 版本等等,之后又做了很多监听工作,看一下源码:

private void basePluginApply(@NonNull Project project) {
    // ... 代码省略
    // 依赖检查
    DependencyResolutionChecks.registerDependencyCheck(project, projectOptions);
    // ... 省略路径检查、模块检查等、构建参数监听器
    // AGP版本检查
    AgpVersionChecker.enforceTheSamePluginVersions(project);
    // 构建流程Task执行的监听器
    RecordingBuildListener buildListener = ProfilerInitializer.init(project, projectOptions);
    ProfileAgent.INSTANCE.register(project.getName(), buildListener);
    threadRecorder = ThreadRecorder.get();
    //... 代码省略
    // 重点
    // 1. 配置项目
    threadRecorder.record(
            ExecutionType.BASE_PLUGIN_PROJECT_CONFIGURE,
            project.getPath(),
            null,
            this::configureProject);
    // 2. 配置扩展
    threadRecorder.record(
            ExecutionType.BASE_PLUGIN_PROJECT_BASE_EXTENSION_CREATION,
            project.getPath(),
            null,
            this::configureExtension);
    // 3. 创建Task
    threadRecorder.record(
            ExecutionType.BASE_PLUGIN_PROJECT_TASKS_CREATION,
            project.getPath(),
            null,
            this::createTasks);
}
复制代码

其中的重点方法我已经标注出来了,分别是配置项目、配置扩展和创建Task。

第三步 配置Project

需要注意的是,此配置并不是对应 Gradle 生命周期的配置,而是针对当前 Project 做一些配置工作。

private void configureProject() {
    // ... 执行大量的Service
    // 依赖版本相关
    Provider<ConstraintHandler.CachedStringBuildService> cachedStringBuildServiceProvider =
            new ConstraintHandler.CachedStringBuildService.RegistrationAction(project)
                    .execute();
    // maven缓存相关
    Provider<MavenCoordinatesCacheBuildService> mavenCoordinatesCacheBuildService =
            new MavenCoordinatesCacheBuildService.RegistrationAction(
                    project, cachedStringBuildServiceProvider)
                    .execute();
    // 依赖库相关
    new LibraryDependencyCacheBuildService.RegistrationAction(project).execute();
    // aapt准备工作
    new Aapt2WorkersBuildService.RegistrationAction(project, projectOptions).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());
    // ... 省略
    createLintClasspathConfiguration(project);
}
复制代码

我对上述代码的理解是创建Task前的准备工作,并且,上面代码中描述的 xxxAction 也很容易让人迷惑,也并不是对应 Task 中的 Action。

第四步 确认扩展

确认扩展对应的方法就是 configureExtension

通常在 app 模块下的 build.gradle 文件中,常常会有诸如此类的配置:

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 JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
}
复制代码

configureExtension 的目的就是为了将此类的脚本信息转化成代码可以识别的信息:

private void configureExtension() {
    // Gradle DSL的帮助类
    DslServices dslServices = globalScope.getDslServices();
    final NamedDomainObjectContainer<BaseVariantOutput> buildOutputs =
            project.container(BaseVariantOutput.class);
    // ... 代码省略
    // ... variant 的工厂类以及管理等等
    variantFactory = createVariantFactory(projectServices, globalScope);
    variantInputModel =
            new LegacyVariantInputManager(
                    dslServices,
                    variantFactory.getVariantType(),
                    new SourceSetManager(
                            project,
                            isPackagePublished(),
                            dslServices,
                            new DelayedActionsExecutor()));
    // 创建扩展
    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);
    // ... 
}
复制代码

简单看一下代码即可,发现大部分的代码都跟 variant 和扩展相关。

再关注一下生成的扩展,BasePlugin#createExtension 是个抽象方法,最终交给了 AppPlugin#createExtension 方法:

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));
}
复制代码

乍看似乎还是不太熟悉,但是如果开发过插件,你一定知道 AppExtension,它可以获取到上面提及的 build.gradle 下的 android {} 中的任何信息。

第五步 创建Task

这应该是最重要的一步了,创建 Task 就在 BasePlugin#createTasks 方法:

private void createTasks() {
    // 注册跟Variant不相关的任务
    threadRecorder.record(
            ExecutionType.TASK_MANAGER_CREATE_TASKS,
            project.getPath(),
            null,
            () ->
                    TaskManager.createTasksBeforeEvaluate(
                            globalScope,
                            variantFactory.getVariantType(),
                            extension.getSourceSets()));
    // 等到Gradle配置阶段完成后,注册跟Variant相关的任务
    project.afterEvaluate(
            CrashReporting.afterEvaluate(
                    p -> {
                        variantInputModel.getSourceSetManager().runBuildableArtifactsActions();
                        threadRecorder.record(
                                ExecutionType.BASE_PLUGIN_CREATE_ANDROID_TASKS,
                                project.getPath(),
                                null,
                                this::createAndroidTasks);
                    }));
}
复制代码

这个方法里面主要有两个方法:

  • TaskManager#createTasksBeforeEvaluate: 静态方法表示在 Project 配置前,会创建一批 Task。
  • createAndroidTasks:注册了一个配置生命周期完成后的回调,等到 Project 配置完成后,Variant 已经确定完毕,又会创建一批 Task。

TaskManager#createTasksBeforeEvaluate 里面是一大段注册 Task 的代码,感兴趣可以自己查看源码。

第六步 配置完成后创建Task

等 Project 进入配置生命周期的回调,进入方法 createAndroidTasks

final void createAndroidTasks() {
    if (extension.getCompileSdkVersion() == null) {
        // ... compileSdkVersion 相关
    }
    // ...
    // 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.");
    }
    // ...

    // 设置一些配置
    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);
    // 注释一 创建Variant
    variantManager.createVariants();
    List<ComponentInfo<VariantT, VariantPropertiesT>> variants =
            variantManager.getMainComponents();
    TaskManager<VariantT, VariantPropertiesT> taskManager =
            createTaskManager(
                    variants,
                    variantManager.getTestComponents(),
                    !variantInputModel.getProductFlavors().isEmpty(),
                    globalScope,
                    extension,
                    threadRecorder);
    // 注释二 创建Task
    taskManager.createTasks();

    // ...

    // 注释三 创建 Task configure compose related tasks.
    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();
}
复制代码

首先,从注释一中可以看出,所有的 Variant 在这一步已经创建完成了。

接着,从注释二和注释三我们可以看出,createAndroidTasks 先后两次使用 taskManager 创建 Task。

第七步 TaskManager第一次创建多个Task

第一次创建 Task 使用的 TaskManager#createTasks 方法,点进这个方法:

public void createTasks() {
    // lint相关的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();
    // 重点,遍历VariantCreate tasks for all variants (main and tests)
    for (ComponentInfo<VariantT, VariantPropertiesT> variant : variants) {
        createTasksForVariant(variant, variants);
    }
    // Test相关的Task
    for (ComponentInfo<
            TestComponentImpl<? extends TestComponentPropertiesImpl>,
            TestComponentPropertiesImpl>
            testComponent : testComponents) {
        createTasksForTest(testComponent);
    }
    // 信息记录相关的Task
    createReportTasks();
}
复制代码

里面依然注册了很多 Task,Lint、测试和信息记录相关的 Task等。

其中最重要的还是获取在上面创建好的的 Variant,遍历执行 createTasksForVariant 方法,我们看看它为每一个 Variant 注册了哪些方法:

private void createTasksForVariant(
        @NonNull ComponentInfo<VariantT, VariantPropertiesT> variant,
        @NonNull List<ComponentInfo<VariantT, VariantPropertiesT>> variants) {
    // ... 省略
    createAssembleTask(variantProperties);
    if (variantType.isBaseModule()) {
        createBundleTask(variantProperties);
    }
    doCreateTasksForVariant(variant, variants);
}
复制代码

大家对 createAssembleTask 这个方法肯定很熟悉,因为我们每次打包都是使用的 assembleDebug 或者 assembleRelease 这样的命令,这个方法就是创建 Assemble 对应的 Task。

doCreateTasksForVariant 方法就是创建跟 Variant 相关 Task 的方法,不过在 TaskManager 中,它是一个抽象方法,交给了 ApplicationTaskManager 去实现。

那么它里面到底创建了哪些 Task 呢?往后面翻,后面的图片会告诉你!

第八步 TaskManager第二次创建多个Task

第二次创建多个 Task 调用的是 TaskManager#createPostApiTasks 方法,主要跟 ViewBinding、DataBinding 和 Kotlin 编译相关的 Task,感兴趣的可以看一下。

这里就不一一和同学们分析了,直接看图:

Task过程@2x.png

简单的解释一下:

  • 蓝色的:Gradle 配置阶段前 createTasksBeforeEvaluate 注册的 Task
  • 橙色:Gradle 配置阶段完成后创建的 Task
  • 红色:重要的 Task
  • 箭头:依赖关系(并不是所有)

当然,我并没有把所有的 Task 都列出来,依赖关系也只把我看见的列出来(代码太多,并没有都阅读)。

如果我们将上面的图片和之前官方的打包流程图结合起来,发现很多都是可以对应起来的:

  1. 前面有 AIDL、Source Code、Resource资源文件这类的处理 Task
  2. 中期有 Class 编译相关、代码混淆相关的 Task
  3. 后期又有创建合并 Dex以及打包 Apk 相关的 Task

而且,Task 之间都有依赖关系(图中并没有展现),比如我通过命令:

./gradlew assembleDebug
复制代码

这个命令会调用 assembleDebug 对应的 Task,在此之前,它会执行完前面依赖的 Task,比如资源处理、编译相关、打包生成我们想要的APK等等。

到这儿,这个源码就分析的差不多了,回到第二步,BasePluginapply 方法里面,还执行了 pluginSpecificApply 方法,不过这个方法是一个空方法。

总结

这片文章的目的是希望大家对 AGP 有一个轮廓,AGP 主要做了什么?

可以发现,AGP 中注册的大部分 Task 都是为了打包服务的,每个小的 Task 都是打包这个流水线的螺丝钉。

下篇文章再和大家分析一下 Transform 的流程,下期见!

如果觉得本文不错,「点赞」是最好的肯定!

参考文章:

《🍵补齐Android技能树——从AGP构建过程到APK打包过程》
《【Android 修炼手册】Gradle 篇 -- Gradle 源码分析》

分类:
Android
标签:
收藏成功!
已添加到「」, 点击更改