【Android 修煉手冊】Gradle 篇 -- Android Gradle Plugin 主要流程分析

預備知識

  1. 理解 gradle 的基本開發
  2. 瞭解 gradle task 和 plugin 使用及開發
  3. 瞭解 android gradle plugin 的使用

看完本文能夠達到什麼程度

  1. 瞭解 android gradle plugin 的構建流程
  2. 瞭解 android gradle plugin 的主要 task 的實現
  3. 學會 hook android 構建流程,添加本身想要的功能

閱讀前準備工做

  1. 項目添加 android gradle plugin 依賴
compile 'com.android.tools.build:gradle:3.0.1'
複製代碼

經過這種方式,能夠直接依賴 plugin 的源碼,讀起來比較方便。
2. 官方對照源碼地址 android gradle plugin 源碼地址java

你們能夠直接 clone EasyGradle 項目,把 android-gradle-plugin-source/build.gradle 裏的 implementation 'com.android.tools.build:gradle:3.0.1' 註釋打開就能夠了。android

com.android.application 主要有下面幾個流程: git

plugin流程

1、插件啓動的準備工做

plugin1

在前面講解自定義插件的時候說到過,要定義一個 xxx.properties 文件,裏面聲明插件的入口類,而 xxx 就是 apply plugin 時候使用的 id,這裏要知道 android gradle plugin 的入口類,看 com.android.application.properties 文件就能夠,內容以下:github

implementation-class=com.android.build.gradle.AppPlugin
複製代碼

這裏定義了入口是 AppPlugin,AppPlugin 繼承自 BasePlugin。
AppPlugin 裏沒有作太多的操做,主要是重寫了 createTaskManager 和 createExtension,剩下的大部分工做仍是在 BasePlugin 裏作的。
插件準備工做中主要作的事情:緩存

  1. 檢查插件版本
// method: BasePlugin.apply()
checkPluginVersion();
複製代碼
  1. 檢查 module 是否重名
// method: BasePlugin.apply()
// 方法中會遍歷全部子項目,判斷是否有重複的 id
this.checkModulesForErrors();
複製代碼
  1. 初始化插件信息
// method: BasePlugin.apply()
PluginInitializer.initialize(project, this.projectOptions);
// 建立 Profiler 文件
ProfilerInitializer.init(project, this.projectOptions);
// profiler 信息中寫入 plugin 版本
ProcessProfileWriter.getProject(project.getPath()).setAndroidPluginVersion(Version.ANDROID_GRADLE_PLUGIN_VERSION).setAndroidPlugin(this.getAnalyticsPluginType()).setPluginGeneration(PluginGeneration.FIRST);
複製代碼

2、配置項目

plugin2

配置項目這一階段主要作的事情:bash

  1. 檢查 gradle 版本是否匹配
// method: BasePlugin.configureProject()
this.checkGradleVersion();
複製代碼
  1. 建立 AndroidBuilder和 DataBindingBuilder
  2. 引入 java plugin 和 jacoco plugin
this.project.getPlugins().apply(JavaBasePlugin.class);
this.project.getPlugins().apply(JacocoPlugin.class);
複製代碼
  1. 設置構建完成之後的混存清理工做 添加了 BuildListener,在 buildFinished 回調裏作緩存清理工做

3、配置 Extension

plugin3

實如今 BasePlugin.configureExtension()
這一階段主要作了下面幾個事情:數據結構

  1. 建立 AppExtension,也就是 build.gradle 裏用到的 android {} dsl
this.extension = this.createExtension(...);

// AppPlugin 中實現了 createExtension,建立了 android {} dsl
protected BaseExtension createExtension(...) {
    return (BaseExtension)project.getExtensions().create("android", AppExtension.class, new Object[]{project, projectOptions, instantiator, androidBuilder, sdkHandler, buildTypeContainer, productFlavorContainer, signingConfigContainer, buildOutputs, extraModelInfo});
}
複製代碼
  1. 建立依賴管理,ndk管理,任務管理,variant管理
  2. 註冊新增配置的回調函數,包括 signingConfig,buildType,productFlavor
// BasePlugin.java createExtension()
// map the whenObjectAdded callbacks on the containers.
signingConfigContainer.whenObjectAdded(variantManager::addSigningConfig);
buildTypeContainer.whenObjectAdded(
    buildType -> {
        SigningConfig signingConfig =
                signingConfigContainer.findByName(BuilderConstants.DEBUG);
        buildType.init(signingConfig);
        // addBuildType,會檢查命名是否合法,而後建立 BuildTypeData
        variantManager.addBuildType(buildType);
    });
// addProductFlavor 會檢查命名是否合法,而後建立 ProductFlavorData
productFlavorContainer.whenObjectAdded(variantManager::addProductFlavor);
// VariantManager.java
// addSigningConfig 就是在 signingConfigs 裏新增一個配置
public void addSigningConfig(SigningConfig signingConfig) {
    this.signingConfigs.put(signingConfig.getName(), signingConfig);
}
// VariantManager.java
public void addProductFlavor(CoreProductFlavor productFlavor) {
    String name = productFlavor.getName();
    // checkName 會檢查
    checkName(name, "ProductFlavor");
    if(this.buildTypes.containsKey(name)) {
        throw new RuntimeException("ProductFlavor names cannot collide with BuildType names");
    } else {
        // 獲取源碼位置
        DefaultAndroidSourceSet mainSourceSet = (DefaultAndroidSourceSet)this.extension.getSourceSets().maybeCreate(productFlavor.getName());
        DefaultAndroidSourceSet androidTestSourceSet = null;
        DefaultAndroidSourceSet unitTestSourceSet = null;
        if(this.variantFactory.hasTestScope()) {
            // 獲取單測源碼位置
            androidTestSourceSet = (DefaultAndroidSourceSet)this.extension.getSourceSets().maybeCreate(computeSourceSetName(productFlavor.getName(), VariantType.ANDROID_TEST));
            unitTestSourceSet = (DefaultAndroidSourceSet)this.extension.getSourceSets().maybeCreate(computeSourceSetName(productFlavor.getName(), VariantType.UNIT_TEST));
        }

        // 建立 productFlavorData 對象
        ProductFlavorData<CoreProductFlavor> productFlavorData = new ProductFlavorData(productFlavor, mainSourceSet, androidTestSourceSet, unitTestSourceSet, this.project);
        this.productFlavors.put(productFlavor.getName(), productFlavorData);
    }
}
複製代碼
  1. 建立默認的 debug 簽名,建立 debug 和 release 兩個 buildType
variantFactory.createDefaultComponents(
    buildTypeContainer, productFlavorContainer, signingConfigContainer);
// ApplicationVariantFactory.java
public void createDefaultComponents(...) {
    signingConfigs.create("debug");
    buildTypes.create("debug");
    buildTypes.create("release");
}
複製代碼

4、建立不依賴 flavor 的 task

plugin4

上述準備,配置階段完成之後,就開始建立構建須要的 Task 了,是在 BasePlugin.createTasks() 裏實現的,主要有兩步,建立不依賴 flavor 的 task 和建立構建 task
先看不依賴 flavor 的 task,其實如今 TaskManager.createTasksBeforeEvaluate()。
這裏主要建立了幾個 task,包括 uninstallAll,deviceCheck,connectedCheck,preBuild,extractProguardFiles,sourceSets,assembleAndroidTest,compileLint,lint,lintChecks,cleanBuildCacheresolveConfigAttr,consumeConfigAttr。
這些 task 都是不須要依賴 flavor 數據的公共 task。app

5、建立構建 task

plugin5

在介紹下面的流程以前,先明確幾個概念,flavor,dimension,variant。
在 android gradle plugin 3.x 以後,每一個 flavor 必須對應一個 dimension,能夠理解爲 flavor 的分組,而後不一樣 dimension 裏的 flavor 組合成一個 variant。
舉個例子:ide

flavorDimensions "size", "color"

productFlavors {
    big {
        dimension "size"
    }
    small {
        dimension "size"
    }
    blue {
        dimension "color"
    }
    red {
        dimension "color"
    }
}
複製代碼

上面配置對應生成的 variant 就是 bigBlue,bigRed,smallBlue,smallRed,在這個基礎上,再加上 buildTypes,就是 bigBlueDebug,bigRedDebug,smallBlueDebug,smallRedDebug,bigBlueRelease,bigRedRelease,smallBlueRelease,smallRedRelease。函數

createAndroidTasks 的調用時機和上面不同,是在 project.afterEvaluate 裏調用的,還記得以前文章裏說道的 afterEvaluate 回調麼?這個時候全部模塊配置已經完成了。因此在這個階段能夠獲取到對應的 flavor 以及其餘配置了。
在 BasePlugin.createAndroidTasks 裏,是調用 VariantManager.createAndroidTasks 完成工做的。
建立 task 的時候,會先經過 populateVariantDataList 生成 flavor 相關的數據結構,而後調用 createTasksForVariantData 建立 flavor 對應的 task。
分別看下這兩個方法作的事情
1.populateVariantDataList
在方法裏,會先根據 flavor 和 dimension 建立對應的組合,存放在 flavorComboList 裏,以後調用 createVariantDataForProductFlavors 建立對應的 VariantData。
其中重要的幾個方法:

// 建立 flavor 和 dimension 的組合
List<ProductFlavorCombo<CoreProductFlavor>> flavorComboList =
                    ProductFlavorCombo.createCombinations(
                            flavorDimensionList,
                            flavorDsl);
// 爲每一個組合建立 VariantData
for (ProductFlavorCombo<CoreProductFlavor>  flavorCombo : flavorComboList) {
    //noinspection unchecked
    createVariantDataForProductFlavors(
            (List<ProductFlavor>) (List) flavorCombo.getFlavorList());
}
複製代碼

建立出來的 VariantData 都是 BaseVariantData 的子類,裏面保存了一些 Task,能夠看一下 BaseVariantData 裏的一些重要的結構,對 BaseVariantData 有個大概的瞭解。

public abstract class BaseVariantData implements TaskContainer {
    private final GradleVariantConfiguration variantConfiguration;
    private VariantDependencies variantDependency;
    private final VariantScope scope;
    public Task preBuildTask;
    public Task sourceGenTask;
    public Task resourceGenTask; // 資源處理
    public Task assetGenTask;
    public CheckManifest checkManifestTask; // 檢測manifest
    public AndroidTask<PackageSplitRes> packageSplitResourcesTask; // 打包資源
    public AndroidTask<PackageSplitAbi> packageSplitAbiTask;
    public RenderscriptCompile renderscriptCompileTask; 
    public MergeResources mergeResourcesTask; // 合併資源
    public ManifestProcessorTask processManifest; // 處理 manifest
    public MergeSourceSetFolders mergeAssetsTask; // 合併 assets
    public GenerateBuildConfig generateBuildConfigTask; // 生成 BuildConfig
    public GenerateResValues generateResValuesTask;
    public Sync processJavaResourcesTask;
    public NdkCompile ndkCompileTask; // ndk 編譯
    public JavaCompile javacTask; 
    public Task compileTask;
    public Task javaCompilerTask; // java 文件編譯
    // ...
}
複製代碼

VariantData 裏保存了不少 task,下一步就要建立這些 task。

2.createTasksForVariantData
建立完 variant 數據,就要給 每一個 variantData 建立對應的 task,對應的 task 有 assembleXXXTask,prebuildXXX,generateXXXSource,generateXXXResources,generateXXXAssets,processXXXManifest 等等,重點關注幾個方法:

VariantManager.createAssembleTaskForVariantData() // 建立 assembleXXXTask
TaskManager.createTasksForVariantScope() // 是一個抽象類,具體實如今 ApplicationTaskManager.createTasksForVariantScope()
TaskManager.createPostCompilationTasks() // 建立 .class to dex 的 task, 建立 transformTask,咱們建立的 transform 就是這個階段添加進來的,是在 addCompileTask 裏調用的

// createTasksForVariantScope 是一個抽象方法,具體實如今子類中,能夠看一下 ApplicationTaskManager.createTasksForVariantScope()
// createTasksForVariantScope 裏的實現,若是在業務中有須要查看相關 task 源碼時,能夠來這裏找
void createTasksForVariantScope() {
    this.createCheckManifestTask(tasks, variantScope); // 檢測 manifest
    this.handleMicroApp(tasks, variantScope);
    this.createDependencyStreams(tasks, variantScope);
    this.createApplicationIdWriterTask(tasks, variantScope); // application id 
    this.createMergeApkManifestsTask(tasks, variantScope); // 合併 manifest
    this.createGenerateResValuesTask(tasks, variantScope);
    this.createRenderscriptTask(tasks, variantScope);
    this.createMergeResourcesTask(tasks, variantScope, true); // 合併資源文件
    this.createMergeAssetsTask(tasks, variantScope, (BiConsumer)null); // 合併 assets
    this.createBuildConfigTask(tasks, variantScope); // 生成 BuildConfig
    this.createApkProcessResTask(tasks, variantScope); // 處理資源
    this.createProcessJavaResTask(tasks, variantScope);
    this.createAidlTask(tasks, variantScope); // 處理 aidl
    this.createShaderTask(tasks, variantScope);
    this.createNdkTasks(tasks, variantScope); // 處理 ndk
    this.createExternalNativeBuildJsonGenerators(variantScope);
    this.createExternalNativeBuildTasks(tasks, variantScope);
    this.createMergeJniLibFoldersTasks(tasks, variantScope); // 合併 jni
    this.createDataBindingTasksIfNecessary(tasks, variantScope); // 處理 databinding
    this.addCompileTask(tasks, variantScope); 
    createStripNativeLibraryTask(tasks, variantScope);
    this.createSplitTasks(tasks, variantScope);
    this.createPackagingTask(tasks, variantScope, buildInfoWriterTask); // 打包 apk
    this.createLintTasks(tasks, variantScope); // lint 
}

// createPostCompilationTasks 實現:
// 處理 Android Transform
void createPostCompilationTasks() {
    for (int i = 0, count = customTransforms.size(); i < count; i++) {
        Transform transform = customTransforms.get(i);
        // TransformManager.addTransform 其實是爲 transform 建立了一個 Task
        transformManager
                .addTransform(tasks, variantScope, transform)
                .ifPresent(t -> {
                    if (!deps.isEmpty()) {
                        t.dependsOn(tasks, deps);
                    }
                    // if the task is a no-op then we make assemble task depend on it.
                    if (transform.getScopes().isEmpty()) {
                        variantScope.getAssembleTask().dependsOn(tasks, t);
                    }
                });
    }
}
複製代碼

6、總結

android gradle plugin 的主要流程基本上就結束了,主要流程圖以下所示

pluign-summary

這裏總結幾個要點:

  1. com.android.application 入口類是 AppPlugin,但大部分工做都是在 BasePlugin 裏完成的
  2. build.gradle 裏見到的 android {} dsl 是在 BasePlugin.configureExtension() 裏聲明的
  3. 主要的 task 是在 BasePlugin.createAndroidTasks() 裏生成的
  4. 主要 task 的實現能夠在 TaskManager 中找到
  5. transform 會轉化成 TransformTask

關於我

about
相關文章
相關標籤/搜索