一、源碼依賴
本文基于:
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。