Android Gradle Plugin 原始碼解析(上)

一、原始碼依賴
本文基於:
android gradle plugin版本:
com.android.tools.build:gradle:2.3.0
gradle 版本:4.1
Gradle原始碼總共30個G,為簡單起見,方便大家看原始碼,此處通過gradle依賴的形式來檢視原始碼,依賴原始碼姿勢:
建立一個新工程,app 專案目錄中刪除所有檔案,僅留下gradle檔案,依賴
apply plugin: 'java' sourceCompatibility = 1.8 dependencies { compile gradleApi() compile 'com.android.tools.build:gradle:2.3.0' }

將跟目錄下的gradle檔案,刪除掉gradle依賴
buildscript { repositories { google() jcenter() } dependencies { // compile 'com.android.tools.build:gradle:2.3.0' } }
然後rebuild一下,就可以在External Libraries中檢視到android gradle的原始碼已經依賴了
二、Android Gradle Plugin簡介
我們知道Android gradle plugin是用來構建Android工程的gradle外掛,在Android gradle 外掛中,可以看到app工程和library工程所依賴的plugin是不一樣的
// app 工程 apply plugin: 'com.android.application' // library 工程 apply plugin: 'com.android.library'
而對應填寫andorid塊中所填寫的配置也不同,這就是區分Application和Library的外掛的extension塊
分別為:
app工程 -> AppPlugin -> AppExtension librar工程 -> LibraryPlugin -> LibraryExtension
對應的是AppPlugin和AppExtension,這兩個外掛構建的流程大抵是相同的,只是各自外掛生成的任務不同,接下來我們著重分析Application外掛是如何構建我們的Android應用的
三、AppPlugin的構建流程
我們先看下app工程中gradle的檔案格式
apply plugin: 'com.android.application' android { compileSdkVersion 25 buildToolsVersion '26.0.2' defaultConfig { applicationId "com.zengshaoyi.gradledemo" minSdkVersion 15 targetSdkVersion 25 versionCode project.ext.versionCode versionName project.ext.versionName testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } lintOptions { abortOnError false } }
跟蹤apply方法,其實是進入到
AppPlugin的apply的方法,我們可以看到內部實現是直接呼叫父類BasePlugin的apply方法
protected void apply(@NonNull Project project) { checkPluginVersion(); this.project = project; ExecutionConfigurationUtil.setThreadPoolSize(project); checkPathForErrors(); checkModulesForErrors(); ProfilerInitializer.init(project); threadRecorder = ThreadRecorder.get(); ProcessProfileWriter.getProject(project.getPath()) .setAndroidPluginVersion(Version.ANDROID_GRADLE_PLUGIN_VERSION) .setAndroidPlugin(getAnalyticsPluginType()) .setPluginGeneration(GradleBuildProject.PluginGeneration.FIRST); 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); threadRecorder.record( ExecutionType.BASE_PLUGIN_PROJECT_TASKS_CREATION, project.getPath(), null, this::createTasks); // Apply additional plugins for (String plugin : AndroidGradleOptions.getAdditionalPlugins(project)) { project.apply(ImmutableMap.of("plugin", plugin)); } }
threadRecoirder.recode()是記錄最後一個引數的路徑和執行的時間點,前面做了一些必要性的資訊檢測之前,其實主要做了以下幾件事情:
// 配置專案,設定構建回撥 this::configureProject // 配置Extension this::configureExtension // 建立任務 this::createTasks
::是java 8引入的特性,詳情可以檢視java8特性 ,這裡就是方法的呼叫
configureProject
直接來看原始碼
private void configureProject() { extraModelInfo = new ExtraModelInfo(project); checkGradleVersion(); AndroidGradleOptions.validate(project); // Android SDK處理類 sdkHandler = new SdkHandler(project, getLogger()); // 設定專案評估階段回撥 project.afterEvaluate(p -> { // TODO: Read flag from extension. if (!p.getGradle().getStartParameter().isOffline() && AndroidGradleOptions.getUseSdkDownload(p)) { // 相關配置依賴的下載處理 SdkLibData sdkLibData = SdkLibData.download(getDownloader(), getSettingsController()); dependencyManager.setSdkLibData(sdkLibData); sdkHandler.setSdkLibData(sdkLibData); } }); // 建立AndroidBuilder androidBuilder = new AndroidBuilder( project == project.getRootProject() ? project.getName() : project.getPath(), creator, new GradleProcessExecutor(project), new GradleJavaProcessExecutor(project), extraModelInfo, getLogger(), isVerbose()); // dataBinding的相關處理 dataBindingBuilder = new DataBindingBuilder(); dataBindingBuilder.setPrintMachineReadableOutput( extraModelInfo.getErrorFormatMode() == ExtraModelInfo.ErrorFormatMode.MACHINE_PARSABLE); // Apply the Java and Jacoco plugins. project.getPlugins().apply(JavaBasePlugin.class); project.getPlugins().apply(JacocoPlugin.class); // 給assemble任務新增描述 project.getTasks() .getByName("assemble") .setDescription( "Assembles all variants of all applications and secondary packages."); ...
可以看到 configureProject 方法中在 project.afterEvaluate 設定了回撥,當專案評估結束時,根據專案配置情況,設定 dependece 依賴;建立了 AndroidBuilder 物件,這個物件是用來合併manifest 和建立 dex 等作用,後面在建立任務的過程中會使用到,結下來繼續看 configureProject 的原始碼
// call back on execution. This is called after the whole build is done (not // after the current project is done). // This is will be called for each (android) projects though, so this should support // being called 2+ times. // 設定構建回撥 project.getGradle() .addBuildListener( new BuildListener() { private final LibraryCache libraryCache = LibraryCache.getCache(); @Override public void buildStarted(Gradle gradle) {} @Override public void settingsEvaluated(Settings settings) {} @Override public void projectsLoaded(Gradle gradle) {} @Override public void projectsEvaluated(Gradle gradle) {} @Override public void buildFinished(BuildResult buildResult) { ExecutorSingleton.shutdown(); sdkHandler.unload(); threadRecorder.record( ExecutionType.BASE_PLUGIN_BUILD_FINISHED, project.getPath(), null, () -> { // 當任務執行完成時,清楚dex快取 PreDexCache.getCache() .clear( FileUtils.join( project.getRootProject() .getBuildDir(), FD_INTERMEDIATES, "dex-cache", "cache.xml"), getLogger()); JackConversionCache.getCache() .clear( FileUtils.join( project.getRootProject() .getBuildDir(), FD_INTERMEDIATES, "jack-cache", "cache.xml"), getLogger()); libraryCache.unload(); Main.clearInternTables(); }); } }); // 設定建立有向圖任務回撥 project.getGradle() .getTaskGraph() .addTaskExecutionGraphListener( taskGraph -> { for (Task task : taskGraph.getAllTasks()) { // TransformTask是class編譯成dex的重要任務 if (task instanceof TransformTask) { Transform transform = ((TransformTask) task).getTransform(); if (transform instanceof DexTransform) { PreDexCache.getCache() .load( FileUtils.join( project.getRootProject() .getBuildDir(), FD_INTERMEDIATES, "dex-cache", "cache.xml")); break; } else if (transform instanceof JackPreDexTransform) { JackConversionCache.getCache() .load( FileUtils.join( project.getRootProject() .getBuildDir(), FD_INTERMEDIATES, "jack-cache", "cache.xml")); break; } } } });
這裡在添加了 BuildListener,在 buildFinished 的時候清楚了dex快取,而在任務有向圖建立的回撥中,判斷是否是 DexTransfrom,從而從快取中載入dex。
總結一下 configureProject 做的事情,主要是進行版本有效性的判斷,建立了 AndroidBuilder 物件,並設定了構建流程的回撥來處理依賴和dex的載入和快取清理。
configureExtension
這個階段就是配置 extension 的階段,就是建立我們 android 塊中的可配置的物件
private void configureExtension() { final NamedDomainObjectContainer<BuildType> buildTypeContainer = project.container( BuildType.class, new BuildTypeFactory(instantiator, project, project.getLogger())); final NamedDomainObjectContainer<ProductFlavor> productFlavorContainer = project.container( ProductFlavor.class, new ProductFlavorFactory( instantiator, project, project.getLogger(), extraModelInfo)); final NamedDomainObjectContainer<SigningConfig> signingConfigContainer = project.container(SigningConfig.class, new SigningConfigFactory(instantiator)); extension = createExtension( project, instantiator, androidBuilder, sdkHandler, buildTypeContainer, productFlavorContainer, signingConfigContainer, extraModelInfo); ...
首先建立了 BuildType、ProductFlavor、SigningConfig 三個型別的Container,接著傳入到了createExtension方法中,點入檢視是個抽象的方法,各自的實現在子類中,這裡也就是我們的AppPlugin 中
@NonNull @Override protected BaseExtension createExtension( @NonNull Project project, @NonNull Instantiator instantiator, @NonNull AndroidBuilder androidBuilder, @NonNull SdkHandler sdkHandler, @NonNull NamedDomainObjectContainer<BuildType> buildTypeContainer, @NonNull NamedDomainObjectContainer<ProductFlavor> productFlavorContainer, @NonNull NamedDomainObjectContainer<SigningConfig> signingConfigContainer, @NonNull ExtraModelInfo extraModelInfo) { return project.getExtensions() .create( "android", AppExtension.class, project, instantiator, androidBuilder, sdkHandler, buildTypeContainer, productFlavorContainer, signingConfigContainer, extraModelInfo); }
這裡也就是可以看到我們android塊配置是如何來的了,對應的Extension也確實是AppExtension,繼續檢視 configureExtension 的原始碼
dependencyManager = new DependencyManager( project, extraModelInfo, sdkHandler); ndkHandler = new NdkHandler( project.getRootDir(), null, /* compileSkdVersion, this will be set in afterEvaluate */ "gcc", "" /*toolchainVersion*/); taskManager = createTaskManager( project, androidBuilder, dataBindingBuilder, extension, sdkHandler, ndkHandler, dependencyManager, registry, threadRecorder); variantFactory = createVariantFactory(instantiator, androidBuilder, extension); variantManager = new VariantManager( project, androidBuilder, extension, variantFactory, taskManager, instantiator, threadRecorder); // Register a builder for the custom tooling model ModelBuilder modelBuilder = new ModelBuilder( androidBuilder, variantManager, taskManager, extension, extraModelInfo, ndkHandler, new NativeLibraryFactoryImpl(ndkHandler), getProjectType(), AndroidProject.GENERATION_ORIGINAL); registry.register(modelBuilder); // Register a builder for the native tooling model NativeModelBuilder nativeModelBuilder = new NativeModelBuilder(variantManager); registry.register(nativeModelBuilder);
這一部分主要是建立一些管理類,其中 createTaskManager、createVariantFactory 都是抽象方法,對應的實現類
createTaskManager AppPlugin -> ApplicationTaskManager LibraryPlugin -> LibraryTaskManager createVariantFactory AppPlugin -> ApplicationVariantFactory LibraryPlugin -> LibraryVariantFactory
這裡簡單介紹一下 TaskManager 就是建立具體任務的管理類,app 工程和庫 library 工程所需的構建任務是不同的,後面我們會介紹 app 工程建立的構建任務;VariantFactory 就是我們常說的構建變體的工廠類,主要是生成Variant(構建變體)的物件。我們回到 createExtension 的原始碼中
// map the whenObjectAdded callbacks on the containers. signingConfigContainer.whenObjectAdded(variantManager::addSigningConfig); buildTypeContainer.whenObjectAdded( buildType -> { SigningConfig signingConfig = signingConfigContainer.findByName(BuilderConstants.DEBUG); buildType.init(signingConfig); variantManager.addBuildType(buildType); }); productFlavorContainer.whenObjectAdded(variantManager::addProductFlavor); ... // create default Objects, signingConfig first as its used by the BuildTypes. variantFactory.createDefaultComponents( buildTypeContainer, productFlavorContainer, signingConfigContainer);
這一部分做得事情,配置了 BuildTypeContainer、ProductFlavorContainer、SigningConfigContainer 這三個配置項的 whenObjectAdded 的回撥,每個配置的新增都會加入到 variantManager 中;建立預設配置,下面是 ApplicationVariantFactory 的 createDefaultComponents 程式碼
@Override public void createDefaultComponents( @NonNull NamedDomainObjectContainer<BuildType> buildTypes, @NonNull NamedDomainObjectContainer<ProductFlavor> productFlavors, @NonNull NamedDomainObjectContainer<SigningConfig> signingConfigs) { // must create signing config first so that build type 'debug' can be initialized // with the debug signing config. signingConfigs.create(DEBUG); buildTypes.create(DEBUG); buildTypes.create(RELEASE); }
總結一下 configureExtension 方法的作用,主要是建立 Android 外掛的擴充套件物件,對配置項 BuildType、ProductFlavor、SigningConfig 做了統一的建立和回撥處理, 建立taskManager、variantFactory、variantManager。
createTasks
private void createTasks() { threadRecorder.record( ExecutionType.TASK_MANAGER_CREATE_TASKS, project.getPath(), null, () -> // 在專案評估之前建立任務 taskManager.createTasksBeforeEvaluate( new TaskContainerAdaptor(project.getTasks()))); project.afterEvaluate( project -> threadRecorder.record( ExecutionType.BASE_PLUGIN_CREATE_ANDROID_TASKS, project.getPath(), null, // 在專案評估完成之後建立 androidTask () -> createAndroidTasks(false))); }
這裡主要是分兩塊,一個是在 beforeEvaluate 建立任務;一個是在 afterEvaluate 建立任務。這裡的區別是 AndroidTask 是依賴配置項的配置才能生成相應任務,所以是需要在 afterEvaluate 之後建立,如果對專案評估回撥不理解的話,可以查閱Project文件。beforeEvaluate 建立的任務跟我們編譯沒有太大關係,我們重點檢視一下 afterEvaluate 建立的任務 createAndroidTasks
@VisibleForTesting final void createAndroidTasks(boolean force) { ... threadRecorder.record( ExecutionType.VARIANT_MANAGER_CREATE_ANDROID_TASKS, project.getPath(), null, () -> { // 建立AndroidTasks variantManager.createAndroidTasks(); ApiObjectFactory apiObjectFactory = new ApiObjectFactory( androidBuilder, extension, variantFactory, instantiator); for (BaseVariantData variantData : variantManager.getVariantDataList()) { apiObjectFactory.create(variantData); } }); ... }
我們主要看下variantManager的createAndroidTasks的方法
/** * Variant/Task creation entry point. * * Not used by gradle-experimental. */ public void createAndroidTasks() { variantFactory.validateModel(this); variantFactory.preVariantWork(project); final TaskFactory tasks = new TaskContainerAdaptor(project.getTasks()); if (variantDataList.isEmpty()) { recorder.record( ExecutionType.VARIANT_MANAGER_CREATE_VARIANTS, project.getPath(), null /*variantName*/, this::populateVariantDataList); } // Create top level test tasks. recorder.record( ExecutionType.VARIANT_MANAGER_CREATE_TESTS_TASKS, project.getPath(), null /*variantName*/, () -> taskManager.createTopLevelTestTasks(tasks, !productFlavors.isEmpty())); for (final BaseVariantData<? extends BaseVariantOutputData> variantData : variantDataList) { recorder.record( ExecutionType.VARIANT_MANAGER_CREATE_TASKS_FOR_VARIANT, project.getPath(), variantData.getName(), () -> createTasksForVariantData(tasks, variantData)); } taskManager.createReportTasks(tasks, variantDataList); }
首先判斷 variantDataList 是否是空,如果是空的就會進入到 populateVariantDataList 方法中
/** * Create all variants. */ public void populateVariantDataList() { if (productFlavors.isEmpty()) { createVariantDataForProductFlavors(Collections.emptyList()); } else { List<String> flavorDimensionList = extension.getFlavorDimensionList(); // Create iterable to get GradleProductFlavor from ProductFlavorData. Iterable<CoreProductFlavor> flavorDsl = Iterables.transform( productFlavors.values(), ProductFlavorData::getProductFlavor); // Get a list of all combinations of product flavors. List<ProductFlavorCombo<CoreProductFlavor>> flavorComboList = ProductFlavorCombo.createCombinations( flavorDimensionList, flavorDsl); for (ProductFlavorCombo<CoreProductFlavor> flavorCombo : flavorComboList) { //noinspection unchecked createVariantDataForProductFlavors( (List<ProductFlavor>) (List) flavorCombo.getFlavorList()); } } }
從方法註釋可以看到,這個方法主要的作用就是建立所有的 variants,試想一下該段程式碼會做哪些事情,是否是解析 buildType、productFlavor 配置?
建立構建變體(BuildVariant)
繼續觀察上面的程式碼,可以看到無論是否有配置productFlavor 子項,都會進入到 createVariantDataForProductFlavors 方法。如果有配置的話,通過獲取配置的 flavorDimension 和 productFlavor 陣列,呼叫 ProductFlavorCombo.createCombinations 組合出最後的產品風味陣列 flavorComboList ,最後通過遍歷呼叫 createVariantDataForProductFlavors 方法
/** * Creates VariantData for a specified list of product flavor. * * This will create VariantData for all build types of the given flavors. * * @param productFlavorList the flavor(s) to build. */ private void createVariantDataForProductFlavors( @NonNull List<ProductFlavor> productFlavorList) { ... for (BuildTypeData buildTypeData : buildTypes.values()) { boolean ignore = false; ... if (!ignore) { BaseVariantData<?> variantData = createVariantData( buildTypeData.getBuildType(), productFlavorList); variantDataList.add(variantData); ... } } ... }
看上述程式碼,通過 creatVariantData 方法,將 buildType 和 productFlavor 的作為引數傳入,建立了 variantData,並且加入到了 variantDataList 集合中,這裡我們就是將所有的構建變體集合到了 variantDataList 中。
接著我們返回繼續看 createAndroidTasks 方法
/** * Variant/Task creation entry point. * * Not used by gradle-experimental. */ public void createAndroidTasks() { ... for (final BaseVariantData<? extends BaseVariantOutputData> variantData : variantDataList) { recorder.record( ExecutionType.VARIANT_MANAGER_CREATE_TASKS_FOR_VARIANT, project.getPath(), variantData.getName(), () -> createTasksForVariantData(tasks, variantData)); } ... }
通過上面拿到的variantDataList,遍歷該集合來建立任務
/** * Create tasks for the specified variantData. */ public void createTasksForVariantData( final TaskFactory tasks, final BaseVariantData<? extends BaseVariantOutputData> variantData) { final BuildTypeData buildTypeData = buildTypes.get( variantData.getVariantConfiguration().getBuildType().getName()); if (buildTypeData.getAssembleTask() == null) { // 建立assemble + buildType任務 buildTypeData.setAssembleTask(taskManager.createAssembleTask(tasks, buildTypeData)); } // Add dependency of assemble task on assemble build type task. tasks.named("assemble", new Action<Task>() { @Override public void execute(Task task) { assert buildTypeData.getAssembleTask() != null; // 將 assemble 任務依賴於我們的 assemble + buildType 任務 task.dependsOn(buildTypeData.getAssembleTask().getName()); } }); VariantType variantType = variantData.getType(); // 根據 variantData 建立 assemble + flavor + buildType 任務 createAssembleTaskForVariantData(tasks, variantData); if (variantType.isForTesting()) { ... } else { // 根據 variantData 建立一系列任務 taskManager.createTasksForVariantData(tasks, variantData); } }
首先會先根據 buildType 資訊建立 assemble + buildType 的任務,可以看下taskManager. createAssembleTask裡的程式碼
@NonNull public AndroidTask<DefaultTask> createAssembleTask( @NonNull TaskFactory tasks, @NonNull VariantDimensionData dimensionData) { final String sourceSetName = StringHelper.capitalize(dimensionData.getSourceSet().getName()); return androidTasks.create( tasks, // 設定任務名字為 assembleXXX "assemble" + sourceSetName, assembleTask -> { // 設定描述和任務組 assembleTask.setDescription("Assembles all " + sourceSetName + " builds."); assembleTask.setGroup(BasePlugin.BUILD_GROUP); }); }
建立完任務之後,將assemble任務依賴於我們的assembleXXX任務,隨後呼叫 createAssembleTaskForVariantData 方法,此方法是建立 assemble + flavor + buildType 任務,流程多了 productFlavor 任務的建立,這裡就不贅述了。後面會執 createTasksForVariantData,這個方法就是根據 variant 生成一系列 Android 構建所需任務(後面會詳細介紹),回到 createAndroidTasks 方法中
threadRecorder.record( ExecutionType.VARIANT_MANAGER_CREATE_ANDROID_TASKS, project.getPath(), null, () -> { variantManager.createAndroidTasks(); ApiObjectFactory apiObjectFactory = new ApiObjectFactory( androidBuilder, extension, variantFactory, instantiator); for (BaseVariantData variantData : variantManager.getVariantDataList()) { // 建立variantApi,新增到extensions中 apiObjectFactory.create(variantData); } });
最後就遍歷 variantDataList 通過 ApiObjectFactory 建立 variantApi,新增到 extensions 中。至此,我們就已經將配置的構建變種任務已經新增到我們的任務列表中,並形成了相關依賴。
一篇文太長,還有一半下一章發出來。
最後
感謝你到這裡,喜歡的話請幫忙點個贊讓更多需要的人看到哦。更多Android進階技術,面試資料整理分享,職業生涯規劃,產品,思維,行業觀察,談天說地。可以加Android架構師群;701740775。