總聽說AGP,它到底做了什么?

前言

故事的開始是這樣的。

之前閱讀《Android開發(fā)高手課》的時候,里面啟動優(yōu)化一欄有講到 systrace + 函數(shù)插樁 是不錯的卡頓排查方式。

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

使用其中的 Demo 進行學(xué)習的時候,發(fā)現(xiàn)將 AGP(Android Gradle Plugin,Android Gradle 打包插件) 升級到 4.0.0 以后,Demo 就不管用了。

分析了一下 Demo,發(fā)現(xiàn)代碼中沒有使用直接注冊 Transform 的方式進行插樁,而是獲取 transformClassesWithDexBuilderForxxx 對應(yīng)的 Task,通過反射的方式將該 Task 中的 Transform 設(shè)置為當前實現(xiàn)的 Transform:

Transform

那為什么在 AGP 4.0.0 的時候,這種方式就不行了呢?

我們都知道,AS 在構(gòu)建代碼的過程中會將執(zhí)行的 Task 都打印到 Build 窗口中,通過觀察不同版本的 AGP 的構(gòu)建過程,發(fā)現(xiàn) 4.0.0 的構(gòu)建過程中沒有打印 transformClassesWithDexBuilderForxxx。

啊,這...

一個好端端的 Task 說沒了就沒了,好吧,AGP 源碼見!

這邊文章先和大家簡單分析一下 AGP 的作用,后續(xù)的文章再和大家分析 Transform 的源碼。

一、基礎(chǔ)準備

在分析源碼之前,我想你應(yīng)該對 Android 打包流程已經(jīng)有基礎(chǔ)的了解,至少了解了下圖的打包過程:

否則你有可能不了解下文中的專業(yè)術(shù)語。

二、AGP源碼的打開方式

看 AGP 代碼的時候,我一直糾結(jié)要不要下載 AGP 的源碼,后來聽同事大佬建議,直接使用了項目依賴的代碼進行分析。

主要的原因有兩點:

  1. AGP 的源碼太大了,有30g,并且版本已經(jīng)很舊了
  2. 使用項目依賴的 AGP 代碼很簡單

只要在項目中加入

implementation "com.android.tools.build:gradle:4.1.1"

即可查看。

三、代碼分析

順便說一下,AGP 的版本是 4.1.1。

第一步 尋找AppPlugin

在 AS 中,如果創(chuàng)建了一個項目,默認在主模塊下面添加:

apply plugin: 'com.android.application'

自定義過 Plugin 的小伙伴都知道,源碼中一定有一個 com.android.application.properties 文件與之相對應(yīng),這便是我們 Plugin 的入口了。

全局搜 com.android.application,打開 com.android.application.properties,內(nèi)容是:

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

按「Command」按鈕點擊源碼,發(fā)現(xiàn) AppPlugin 里面又聲明了一個 Plugin,最終跳到了:

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

包名與之前的不一樣,這才是我們的最終入口。

各位同學(xué)有沒有這樣的疑惑,我給加上 apply plugin: com.android.application,那這段代碼什么時候調(diào)用呢?

不知道大家有沒有注意到,每次改動 build.gradle 文件的時候,AS 都會讓我們點擊 「Sync Now」按鈕,點擊完了,就會觸發(fā) Gradle 中的配置過程,最終會運行 Plugin#apply 方法,大家可以自定義 Plugin 的時候驗證一下。

第二步 AppPlugin

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

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

這里我們只需要關(guān)注方法塊里面的兩個方法 basePluginApplypluginSpecificApply

進入重點方法 basePluginApply 方法,這個方法的前期做了很多的檢查工作,包括路徑、版本和 AGP 版本等等,之后又做了很多監(jiān)聽工作,看一下源碼:

private void basePluginApply(@NonNull Project project) {
    // ... 代碼省略
    // 依賴檢查
    DependencyResolutionChecks.registerDependencyCheck(project, projectOptions);
    // ... 省略路徑檢查、模塊檢查等、構(gòu)建參數(shù)監(jiān)聽器
    // AGP版本檢查
    AgpVersionChecker.enforceTheSamePluginVersions(project);
    // 構(gòu)建流程Task執(zhí)行的監(jiān)聽器
    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. 創(chuàng)建Task
    threadRecorder.record(
            ExecutionType.BASE_PLUGIN_PROJECT_TASKS_CREATION,
            project.getPath(),
            null,
            this::createTasks);
}

其中的重點方法我已經(jīng)標注出來了,分別是配置項目、配置擴展和創(chuàng)建Task。

第三步 配置Project

需要注意的是,此配置并不是對應(yīng) Gradle 生命周期的配置,而是針對當前 Project 做一些配置工作。

private void configureProject() {
    // ... 執(zhí)行大量的Service
    // 依賴版本相關(guān)
    Provider<ConstraintHandler.CachedStringBuildService> cachedStringBuildServiceProvider =
            new ConstraintHandler.CachedStringBuildService.RegistrationAction(project)
                    .execute();
    // maven緩存相關(guān)
    Provider<MavenCoordinatesCacheBuildService> mavenCoordinatesCacheBuildService =
            new MavenCoordinatesCacheBuildService.RegistrationAction(
                    project, cachedStringBuildServiceProvider)
                    .execute();
    // 依賴庫相關(guān)
    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相關(guān)
    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);
    // 消息打印服務(wù)注冊
    MessageReceiverImpl messageReceiver =
            new MessageReceiverImpl(
                    SyncOptions.getErrorFormatMode(projectOptions),
                    projectServices.getLogger());
    // ... 省略
    createLintClasspathConfiguration(project);
}

我對上述代碼的理解是創(chuàng)建Task前的準備工作,并且,上面代碼中描述的 xxxAction 也很容易讓人迷惑,也并不是對應(yīng) Task 中的 Action。

第四步 確認擴展

確認擴展對應(yīng)的方法就是 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 的目的就是為了將此類的腳本信息轉(zhuǎn)化成代碼可以識別的信息:

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()));
    // 創(chuàng)建擴展
    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);
    // ... 
}

簡單看一下代碼即可,發(fā)現(xiàn)大部分的代碼都跟 variant 和擴展相關(guān)。

再關(guān)注一下生成的擴展,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));
}

乍看似乎還是不太熟悉,但是如果開發(fā)過插件,你一定知道 AppExtension,它可以獲取到上面提及的 build.gradle 下的 android {} 中的任何信息。

第五步 創(chuàng)建Task

這應(yīng)該是最重要的一步了,創(chuàng)建 Task 就在 BasePlugin#createTasks 方法:

private void createTasks() {
    // 注冊跟Variant不相關(guān)的任務(wù)
    threadRecorder.record(
            ExecutionType.TASK_MANAGER_CREATE_TASKS,
            project.getPath(),
            null,
            () ->
                    TaskManager.createTasksBeforeEvaluate(
                            globalScope,
                            variantFactory.getVariantType(),
                            extension.getSourceSets()));
    // 等到Gradle配置階段完成后,注冊跟Variant相關(guān)的任務(wù)
    project.afterEvaluate(
            CrashReporting.afterEvaluate(
                    p -> {
                        variantInputModel.getSourceSetManager().runBuildableArtifactsActions();
                        threadRecorder.record(
                                ExecutionType.BASE_PLUGIN_CREATE_ANDROID_TASKS,
                                project.getPath(),
                                null,
                                this::createAndroidTasks);
                    }));
}

這個方法里面主要有兩個方法:

  • TaskManager#createTasksBeforeEvaluate: 靜態(tài)方法表示在 Project 配置前,會創(chuàng)建一批 Task。
  • createAndroidTasks:注冊了一個配置生命周期完成后的回調(diào),等到 Project 配置完成后,Variant 已經(jīng)確定完畢,又會創(chuàng)建一批 Task。

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

第六步 配置完成后創(chuàng)建Task

等 Project 進入配置生命周期的回調(diào),進入方法 createAndroidTasks

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

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

    // ...

    // 注釋三 創(chuàng)建 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 在這一步已經(jīng)創(chuàng)建完成了。

接著,從注釋二和注釋三我們可以看出,createAndroidTasks 先后兩次使用 taskManager 創(chuàng)建 Task。

第七步 TaskManager第一次創(chuàng)建多個Task

第一次創(chuàng)建 Task 使用的 TaskManager#createTasks 方法,點進這個方法:

public void createTasks() {
    // lint相關(guān)的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相關(guān)的Task
    for (ComponentInfo<
            TestComponentImpl<? extends TestComponentPropertiesImpl>,
            TestComponentPropertiesImpl>
            testComponent : testComponents) {
        createTasksForTest(testComponent);
    }
    // 信息記錄相關(guān)的Task
    createReportTasks();
}

里面依然注冊了很多 Task,Lint、測試和信息記錄相關(guān)的 Task等。

其中最重要的還是獲取在上面創(chuàng)建好的的 Variant,遍歷執(zhí)行 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 這樣的命令,這個方法就是創(chuàng)建 Assemble 對應(yīng)的 Task。

doCreateTasksForVariant 方法就是創(chuàng)建跟 Variant 相關(guān) Task 的方法,不過在 TaskManager 中,它是一個抽象方法,交給了 ApplicationTaskManager 去實現(xiàn)。

那么它里面到底創(chuàng)建了哪些 Task 呢?往后面翻,后面的圖片會告訴你!

第八步 TaskManager第二次創(chuàng)建多個Task

第二次創(chuàng)建多個 Task 調(diào)用的是 TaskManager#createPostApiTasks 方法,主要跟 ViewBinding、DataBinding 和 Kotlin 編譯相關(guān)的 Task,感興趣的可以看一下。

這里就不一一和同學(xué)們分析了,直接看圖:

簡單的解釋一下:

  • 藍色的:Gradle 配置階段前 createTasksBeforeEvaluate 注冊的 Task
  • 橙色:Gradle 配置階段完成后創(chuàng)建的 Task
  • 紅色:重要的 Task
  • 箭頭:依賴關(guān)系(并不是所有)

當然,我并沒有把所有的 Task 都列出來,依賴關(guān)系也只把我看見的列出來(代碼太多,并沒有都閱讀)。

如果我們將上面的圖片和之前官方的打包流程圖結(jié)合起來,發(fā)現(xiàn)很多都是可以對應(yīng)起來的:

  1. 前面有 AIDL、Source Code、Resource資源文件這類的處理 Task
  2. 中期有 Class 編譯相關(guān)、代碼混淆相關(guān)的 Task
  3. 后期又有創(chuàng)建合并 Dex以及打包 Apk 相關(guān)的 Task

而且,Task 之間都有依賴關(guān)系(圖中并沒有展現(xiàn)),比如我通過命令:

./gradlew assembleDebug

這個命令會調(diào)用 assembleDebug 對應(yīng)的 Task,在此之前,它會執(zhí)行完前面依賴的 Task,比如資源處理、編譯相關(guān)、打包生成我們想要的APK等等。

到這兒,這個源碼就分析的差不多了,回到第二步,BasePluginapply 方法里面,還執(zhí)行了 pluginSpecificApply 方法,不過這個方法是一個空方法。

總結(jié)

這片文章的目的是希望大家對 AGP 有一個輪廓,AGP 主要做了什么?

可以發(fā)現(xiàn),AGP 中注冊的大部分 Task 都是為了打包服務(wù)的,每個小的 Task 都是打包這個流水線的螺絲釘。

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

如果覺得本文不錯,「點贊」是最好的肯定!

參考文章:

《??補齊Android技能樹——從AGP構(gòu)建過程到APK打包過程》
《【Android 修煉手冊】Gradle 篇 -- Gradle 源碼分析》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容