天天看點

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。