預備知識

  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

【Android 修鍊手冊】系列內容 每周五更新

歡迎關注下面賬號,獲取更新:

微信搜索公眾號: ZYLAB

知乎


推薦閱讀:
相关文章