Gradle之Android Gradle Plugin 主要流程分析(二)


【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 源碼地址

大家可以直接 clone EasyGradle 項目,把 android-gradle-plugin-source/build.gradle 里的 implementation 'com.android.tools.build:gradle:3.0.1' 注釋打開就可以了。

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

一、插件啟動的准備工作

 

在前面講解自定義插件的時候說到過,要定義一個 xxx.properties 文件,里面聲明插件的入口類,而 xxx 就是 apply plugin 時候使用的 id,這里要知道 android gradle plugin 的入口類,看 com.android.application.properties 文件就可以,內容如下:
implementation-class=com.android.build.gradle.AppPlugin

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

1.檢查插件版本

// method: BasePlugin.apply()
checkPluginVersion();

2.檢查 module 是否重名

// method: BasePlugin.apply()
// 方法中會遍歷所有子項目,判斷是否有重復的 id
this.checkModulesForErrors();

3.初始化插件信息

// 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);

二、配置項目

 

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

1.檢查 gradle 版本是否匹配

// method: BasePlugin.configureProject()
this.checkGradleVersion();

2.創建 AndroidBuilder和 DataBindingBuilder

3.引入 java plugin 和 jacoco plugin

this.project.getPlugins().apply(JavaBasePlugin.class);
this.project.getPlugins().apply(JacocoPlugin.class);

4.設置構建完成以后的混存清理工作 添加了 BuildListener,在 buildFinished 回調里做緩存清理工作

三、配置 Extension

 

 實現在 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});
}

2.創建依賴管理,ndk管理,任務管理,variant管理

3.注冊新增配置的回調函數,包括 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);
    }
}

4.創建默認的 debug 簽名,創建 debug 和 release 兩個 buildType

variantFactory.createDefaultComponents(
    buildTypeContainer, productFlavorContainer, signingConfigContainer);
// ApplicationVariantFactory.java
public void createDefaultComponents(...) {
    signingConfigs.create("debug");
    buildTypes.create("debug");
    buildTypes.create("release");
}

四、創建不依賴 flavor 的 task

 

上述准備,配置階段完成以后,就開始創建構建需要的 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。

五、創建構建 task

 

 

在介紹下面的流程之前,先明確幾個概念,flavor,dimension,variant。
在 android gradle plugin 3.x 之后,每個 flavor 必須對應一個 dimension,可以理解為 flavor 的分組,然后不同 dimension 里的 flavor 組合成一個 variant。
舉個例子:
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);
                    }
                });
    }
}

六、總結

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

這里總結幾個要點:

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


 
 

 

 



 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM