Gradle 進階 第七篇
鍥而不舍,金石可鏤
Gradle Project 上卷
Gradle Project 對應(yīng)一個模塊,對于一個多模塊構(gòu)建的項目,它會有 RootProject,和所有 SubProject。這些都是在 setting.gradle 中配置的:
include ':lib'
include ':app'
rootProject.name = "GradleDebug"
Project 的實現(xiàn)類是 DefaultProject。創(chuàng)建它的類是 ProjectFactory。在解析完 setting 文件之后便創(chuàng)建了所有的 Project,并且把 RootProject 設(shè)置成為所有 SubProject 的 Parent。創(chuàng)建代碼如下。
@Override
public DefaultProject createProject(GradleInternal gradle, ProjectDescriptor projectDescriptor, ProjectInternal parent, ClassLoaderScope selfClassLoaderScope, ClassLoaderScope baseClassLoaderScope) {
File buildFile = projectDescriptor.getBuildFile();
TextResource resource = textFileResourceLoader.loadFile("build file", buildFile);
ScriptSource source = new TextResourceScriptSource(resource);
DefaultProject project = instantiator.newInstance(DefaultProject.class,
projectDescriptor.getName(),
parent,
projectDescriptor.getProjectDir(),
buildFile,
source,
gradle,
gradle.getServiceRegistryFactory(),
selfClassLoaderScope,
baseClassLoaderScope
);
project.beforeEvaluate(new Action<Project>() {
@Override
public void execute(Project project) {
NameValidator.validate(project.getName(), "project name", DefaultProjectDescriptor.INVALID_NAME_IN_INCLUDE_HINT);
}
});
if (parent != null) {
parent.addChildProject(project);
}
projectRegistry.addProject(project);
return project;
}
TextResource resource = textFileResourceLoader.loadFile("build file", buildFile); 表示在創(chuàng)建 DefaultProject 的時候需要獲得 build.gradle 文件。最終把生成的 DefaultProject 文件加入到注冊表中。在這里需要注意一點,Project 的創(chuàng)建早于對應(yīng)的 "build.gradle" 的編譯。
DefaultProject 中維護了許多重要的成員變量。這里展示出部分,并通過講解這些成員變量來剖析一下 Project 扮演的角色。這里我放一張圖來加深理解:

private TaskContainerInternal taskContainer;
private DependencyHandler dependencyHandler;
private ConfigurationContainer configurationContainer;
private ArtifactHandler artifactHandler;
private ExtensibleDynamicObject extensibleDynamicObject;
TaskContainer
TaskContainer 主要作用是管理一些列的 Task,TaskContainer 本身是一個接口,同時繼承了 NamedDomainObjectCollection 接口,TaskContainerInternal 也是一個接口,繼承了 TaskContainer,真正的實現(xiàn)類是 DefaultTaskContainer。
public class DefaultTaskContainer extends DefaultTaskCollection<Task> implements TaskContainerInternal {
private Task doCreate(Map<String, ?> options, final Action<? super Task> configureAction) {
...
}
private <T extends Task> T doCreate(final String name, final Class<T> type, @Nullable final Object[] constructorArgs, final Action<? super T> configureAction) throws InvalidUserDataException {
...
}
}
Task 的創(chuàng)建就是通過這兩個重要的方法創(chuàng)建的,后面的 Task 章節(jié)會詳細的講解。這里會簡單用個例子來作一個引子:
task clean(type: Delete) {
delete rootProject.buildDir
}
在 Gradle 的腳本文件中,直接調(diào)用 task 函數(shù)來創(chuàng)建。上述例子編譯成的.class 文件,用IJ Ide 打開,有這么一段:
acallsite[1].callCurrent(this, ScriptBytecodeAdapter.createMap(new Object[] {
"type", org/gradle/api/tasks/Delete
}), "clean", new _run_closure2(this, this))
其中 acallsite[1] 是 "task",結(jié)合我們在第二篇文章里的方法查找機制,這個方法最終映射到 DefaultProject 的下面這個方法上:
@Override
public Task task(Map options, String task, Closure configureClosure) {
return taskContainer.create(addMaps(options, singletonMap(Task.TASK_NAME, task))).configure(configureClosure);
}
這里的 taskContainer 的 create 最終調(diào)用上面所說的 doCreate.
@Override
public Task create(Map<String, ?> options) {
assertMutable("create(Map<String, ?>)");
return doCreate(options, Actions.doNothing());
}
這里需要注意一點 { delete rootProject.buildDir } 這個 closure 的作用是為了配置創(chuàng)建出來的 Task。細節(jié)同樣在后續(xù) Task 章節(jié)里講解。
在 doCreate 中會把創(chuàng)建好的 task 加入集合中,同時也會根據(jù)設(shè)置 task 的一些屬性來配置相關(guān)依賴等等。
DependencyHandler
DependencyHandler 的工作主要是幫助 Project 來管理依賴,DefaultDependencyHandler 實現(xiàn)了 DependencyHandler 的接口,DefaultDependencyHandler 是抽象類,Gradle 會在運行時通過 ASM 創(chuàng)建DefaultDependencyHandler_Decorated 類,并實例化。這塊細節(jié)也會在后面的一章講解。
這里舉一個栗子,我們經(jīng)常會為項目添加依賴,在 Gradle 腳本文件中往往是下面這種形式:
dependencies {
implementation 'com.android.tools.build:gradle:3.1.0'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
這個方法映射到 DefaultProject 里的方法:
@Override
public void dependencies(Closure configureClosure) {
ConfigureUtil.configure(configureClosure, getDependencies());
}
其中 getDependencies() 返回的便是 DependencyHandler。這里需要講的一點是,在 dependencies 代碼塊里面的 implementation 'com.android.tools.build:gradle:3.1.0' 實際上是一個函數(shù)調(diào)用,函數(shù)名稱是 implementation,參數(shù)是 'com.android.tools.build:gradle:3.1.0',同時需要注意一點,這里的函數(shù)名其實是和 Configuration 對應(yīng)的。
但是事實上,DefaultDependencyHandler(_Decorated) 中并沒有類似于 'implementaion' 這樣的函數(shù),我在前文講過 BeanDynamicObject,BeanDynamicObject 的作用是使用常規(guī)反射來提供對bean的屬性和方法的訪問,這里的 bean 就是 DefaultDependencyHandler(_Decorated),Gradel 在沒有直接找到方法調(diào)用時,還會有一些備用邏輯,如下:
if (bean instanceof MethodMixIn) {
// If implements MethodMixIn, do not attempt to locate opaque method, as this is expensive
MethodMixIn methodMixIn = (MethodMixIn) bean;
return methodMixIn.getAdditionalMethods().tryInvokeMethod(name, arguments);
}
對于 MethodMixIn 類型的 bean,會調(diào)用 getAdditionalMethods() 來繼續(xù)查找。
public MethodAccess getAdditionalMethods() {
return dynamicMethods;
}
...
dynamicMethods = new DynamicAddDependencyMethods(configurationContainer, new DirectDependencyAdder());
DefaultDependencyHandler(_Decorated) 繼承自 MethodMixIn 接口,提供的額外的方法搜索路徑 DynamicAddDependencyMethods。
class DynamicAddDependencyMethods implements MethodAccess {
...
@Override
public boolean hasMethod(String name, Object... arguments) {
return arguments.length != 0 && configurationContainer.findByName(name) != null;
}
@Override
public DynamicInvokeResult tryInvokeMethod(String name, Object... arguments) {
if (arguments.length == 0) {
return DynamicInvokeResult.notFound();
}
Configuration configuration = configurationContainer.findByName(name);
if (configuration == null) {
return DynamicInvokeResult.notFound();
}
List<?> normalizedArgs = CollectionUtils.flattenCollections(arguments);
if (normalizedArgs.size() == 2 && normalizedArgs.get(1) instanceof Closure) {
return DynamicInvokeResult.found(dependencyAdder.add(configuration, normalizedArgs.get(0), (Closure) normalizedArgs.get(1)));
} else if (normalizedArgs.size() == 1) {
return DynamicInvokeResult.found(dependencyAdder.add(configuration, normalizedArgs.get(0), null));
} else {
for (Object arg : normalizedArgs) {
dependencyAdder.add(configuration, arg, null);
}
return DynamicInvokeResult.found();
}
}
...
}
可以看到在 tryInvokeMethod 方法里實際上通過 configurationContainer,來找到和調(diào)用函數(shù)名相同的 Configuration。然后通過 dependencyAdder 的add 方法調(diào)用到 DefaultDependencyHandler 中的 doAdd 方法。
private Dependency doAdd(Configuration configuration, Object dependencyNotation, @Nullable Closure configureClosure) {
if (dependencyNotation instanceof Configuration) {
DeprecationLogger.deprecateBehaviour("Adding a Configuration as a dependency is a confusing behavior which isn't recommended.")
.withAdvice("If you're interested in inheriting the dependencies from the Configuration you are adding, you should use Configuration#extendsFrom instead.")
.willBeRemovedInGradle8()
.withDslReference(Configuration.class, "extendsFrom(org.gradle.api.artifacts.Configuration[])")
.nagUser();
return doAddConfiguration(configuration, (Configuration) dependencyNotation);
}
if (dependencyNotation instanceof Provider<?>) {
return doAddProvider(configuration, (Provider<?>) dependencyNotation, configureClosure);
} else {
return doAddRegularDependency(configuration, dependencyNotation, configureClosure);
}
}
后續(xù)的如何解析依賴,以及依賴沖突解決等等問題,在后面會詳細講解。
ConfigurationContaine
在上面講 DependencyHandler 一節(jié)中提到了 ConfigurationContainer。它是一個接口,繼承自 NamedDomainObjectContainer 接口,什么是 NamedDomainObjectContainer,舉個栗子:
configurations {
// declare a "configuration" named "someConfiguration"
someConfiguration
}
dependencies {
// add a project dependency to the "someConfiguration" configuration
someConfiguration project(":lib")
}
就是用戶可以在集合中自定義一些對象,而這些對象可以被加入 Gradle 動態(tài)系統(tǒng)。再舉一個栗子,buildTypes 就提供用戶創(chuàng)建各種各樣的 BuildType:
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
debug{
}
xxx{
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
Gradle 是如何實現(xiàn)這個功能的,我會在后面的文章單獨來講解?;氐秸},這里的 ConfigurationContaine 是管理構(gòu)建時的 Configuration。
以下是官網(wǎng)解釋什么是 Configuration:
Every dependency declared for a Gradle project applies to a specific scope. For example some dependencies should be used for compiling source code whereas others only need to be available at runtime. Gradle represents the scope of a dependency with the help of a Configuration. Every configuration can be identified by a unique name.
為Gradle項目聲明的每個依賴項都應(yīng)用于特定范圍。例如,某些依賴項應(yīng)該用于編譯源代碼,而其他依賴項只需要在運行時可用。Gradle在配置的幫助下表示依賴關(guān)系的范圍。每個配置都可以用一個唯一的名稱來標(biāo)識。
貼一張官網(wǎng)的圖:
