Gradle随记---初识
谈起Gradle开发,自然就需要先从最基础的两个配置文件build.gradle
以及settings.gradle
说起,那其实对应的Gradle API类就是org.gradle.api.Project
和org.gradle.api.initialization.Settings
,而定义在文件中熟悉的闭包配置对应的则是类方法以及各种拓展Extension。
Settings
对于Settings的认识,相信大多数都是停留在settings.gradle
文件中添加include
项目配置,但是能做的不仅于此,我们就此从创建、设置加载来完整了解一遍。
创建
Settings的创建跟org.gradle.initialization.ScriptEvaluatingSettingsProcessor
以及org.gradle.initialization.SettingsFactory
两个类息息相关。
Gradle首先会通过BuildScopeServices
的createSettingsProcessor
方法创建一个SettingsProcessor
,这个类主要是通过ScriptEvaluatingSettingsProcessor
来进行构造,然后借助其process
方法来创建Settings
的实例。
public SettingsInternal process(GradleInternal gradle,
SettingsLocation settingsLocation,
ClassLoaderScope buildRootClassLoaderScope,
StartParameter startParameter) {
Timer settingsProcessingClock = Time.startTimer();
Map<String, String> properties = propertiesLoader.mergeProperties(Collections.<String, String>emptyMap());
TextResourceScriptSource settingsScript = new TextResourceScriptSource(textResourceLoader.loadFile("settings file", settingsLocation.getSettingsFile()));
SettingsInternal settings = settingsFactory.createSettings(gradle, settingsLocation.getSettingsDir(), settingsScript, properties, startParameter, buildRootClassLoaderScope);
applySettingsScript(settingsScript, settings);
LOGGER.debug("Timing: Processing settings took: {}", settingsProcessingClock.getElapsed());
return settings;
}
从上述代码中得知该方法主要做了三件事情:
- 根据
settings.gradle
文件构造TextResourceScriptSource
对象settingsScript
- 通过
settingsFactory
创建一个SettingsInternal
实例 - 调用
applySettingsScript
方法将settings.gradle
中的闭包配置装载到SettingsInternal
实例中
第2和3步都需要用到TextResourceScriptSource
,这个具体作用是什么呢?
TextResourceScriptSource
public class TextResourceScriptSource implements ScriptSource {
private final TextResource resource;
private String className;
...
}
这个类主要作用是通过URI
读取指定的配置脚本源码以及生成对应的className
,这里对应的就是settings.gradle
配置文件,并且作为构造函数参数生成ScriptCompiler
来把脚本文件编译成一个object
对象。
SettingsFactory
再来看下SettingsFactory
的createSettings
方法:
public class SettingsFactory {
...
public SettingsInternal createSettings(GradleInternal gradle, File settingsDir, ScriptSource settingsScript,
Map<String, String> gradleProperties, StartParameter startParameter,
ClassLoaderScope buildRootClassLoaderScope) {
ClassLoaderScope settingsClassLoaderScope = buildRootClassLoaderScope.createChild("settings");
ScriptHandlerInternal settingsScriptHandler = scriptHandlerFactory.create(settingsScript, settingsClassLoaderScope);
DefaultSettings settings = instantiator.newInstance(DefaultSettings.class,
serviceRegistryFactory, gradle,
settingsClassLoaderScope, buildRootClassLoaderScope, settingsScriptHandler,
settingsDir, settingsScript, startParameter
);
...
return settings;
}
}
通过Instantiator
创建了一个DefaultSettings
类型的实例。
到这里settings.gradle
对应的Settings
实例就创建完毕了,接着就是把定义在文件中的闭包设置加载到实例中,整个过程就结束了。
配置
还记得上面的SettingsInternal
中process
第三步吗,那就是对settings.gradle
文件中定义的配置进行应用。
private void applySettingsScript(TextResourceScriptSource settingsScript, final SettingsInternal settings) {
ClassLoaderScope settingsClassLoaderScope = settings.getClassLoaderScope();
ScriptPlugin configurer = configurerFactory.create(settingsScript, settings.getBuildscript(), settingsClassLoaderScope, settings.getRootClassLoaderScope(), true);
configurer.apply(settings);
}
通过ScriptPluginFactory
生成ScriptPlugin
,然后调用其apply
方法加载对应的配置。
Settings.gradle
对于settings.gradle
文件来说,我们最常见的配置如下:
include ':app'
那除此之外还能干什么呢?
其实,这里主要作用是用来定义这个工程有哪些module
,并且定义其ProjectName
以及对应的工程目录。那这里我们可以用来在整个工程的Project
配置装载前做一些初始化的事情,例如生成动态module
、配置构建缓存、配置module
对应的BuildFile
、插件冲突配置等。
具体能应用的配置可以查看org.gradle.api.initialization.Settings
公开的API,每一个方法对应的都是一种配置选项。
例如,include ':app'
对应的是void include(String... projectPaths)
方法,而project(':annotation')
对应的是ProjectDescriptor project(String path)
方法,更多的脚本配置可以自行查询。
另外,这里也能apply
Gradle插件,会在后续的插件文章中详细说明,至此Settings
就先到此为止了。
Project
对于每一个在settings.gradle
中用include
进行申明配置的module
都对应一个Project
实例,而对project而已,相应的构建配置是在对应项目根目录下的build.gradle
中进行申明。
build.gradle
虽然配置文件中闭包配置对应都是同一个org.gradle.api.Project
中的API,但是因为不同project
所需要apply
的gradle plugin
会有所不同,因此导致看起来配置会稍有差异,尤其rootProject
和project
,但是根源本质是一致的。
我们先从接触最多的project
配置文件开始着手
apply plugin: 'com.android.application'
android {
compileSdkVersion 27
defaultConfig {
applicationId "xx.yy.demo"
minSdkVersion 14
targetSdkVersion 27
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
debug {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:27.1.1'
}
这是最常见的一个配置,简单但是功能齐全,接着我们来根据配置从上到下分析gradle
到底干了什么,配置的选项在编译过程中起什么作用。
- 首先调用
project
的void apply(Map<String, ?> options)
方法把com.android.build.gradle.AppPlugin
插件进行装载,如果apply
的是com.android.library
则对应的是com.android.build.gradle.LibraryPlugin
插件。 - 接下来是熟悉的
andoird{ ... }
,这其实是上述提到的AppPlugin
插件的extension
配置,对应的是com.android.build.gradle.AppExtension
,当然如果是LibraryPlugin
插件则对应的是com.android.build.gradle.LibraryPlugin
,闭包中的每一个配置选项都是对应相应extension
类中的方法。 - 最后是
dependencies{...}
,这里是配置当前module
编译所需要依赖的第三方库,实际对应的是先getDependencies()
获取org.gradle.api.artifacts.dsl.DependencyHandler
对象,然后调用其Dependency add(String configurationName, Object dependencyNotation)
方法对其中定义的依赖配置进行添加。
至此,所对应的配置会在configure project
过程中调用其对应的方法进行配置装载,后续应用到编译过程中。
build.gradle
闭包配置先到此为止,更多配置例如task ...
等其实跟上述一样是调用Task task(String name)
方法,接下来说说project
常用的几个方法。
DependencyHandler
从上述我们得知,增加第三方库依赖是调用Dependency add(String configurationName, Object dependencyNotation)
方法,其中的api
、implementation
、runtimeOnly
等其实是AppPlugin
等插件中预先声明的configurationName
,而dependencyNotation
进行添加后则返回对应的实例对象。例如:
-
fileTree(dir: 'libs', include: ['*.jar'])
---org.gradle.api.internal.artifacts.dependencies.DefaultSelfResolvingDependency
-
project(':xxxx')
---org.gradle.api.internal.artifacts.dependencies.DefaultProjectDependency
-
'com.android.support:appcompat-v7:27.1.1'
---org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency
因此,我们也可以在自己的插件中自定义configurationName
,例如:
project.beforeEvaluate {
Configuration customConfiguration = it.configurations.create("configurationName")
customConfiguration.allDependencies.all{dependency ->
//dependency进行处理
...
}
}
beforeEvaluate / afterEvaluate
这两个方法是project
最常用的两个方法:
-
beforeEvaluate
是设置project
进行evaluate
也就是对build.gradle
进行配置加载之前的回调,适合apply plugin
、定义Configuration
等场景。 -
afterEvaluate
则是project
进行配置加载之后的回调,适合需要根据配置的信息来进行操作的场景,因为在build.gradle
中配置的extension
要在afterEvaluate
才能拿到。
hasProperty / getProperties
从方法名就可以猜到,是对project
定义的属性数据进行操作,也就是闭包配置
ext{
key = "value"
}
好了,project
就先介绍到这了,更多的方法可以自行查询API和对应的注释信息。
后记
project
和settings
的介绍就先到这了,这是gradle
开发的基础,了解清楚就相当于迈过了入门的门槛石,随节会对其中的task
进行深入介绍。