Hello,各位朋友们,小笨鸟又和大家见面啦。不一样于网上泛泛而谈的入门文章只停留在“怎么用”的层次,本篇文章从源码角度去理解gradle。固然,若是你没有看过个人前一篇文章《一篇文章基本看懂gradle》,仍是建议你先看一下,本篇文章的不少知识点会在上篇文章的基础上展开。html
本篇文章会比较深刻,须要费一些脑筋和精力才能理解。建议跟着个人行文思路一步一步理解。本文的篇幅会比较长。阅读大概须要花费半个小时。阅读本文将会了解:java
其中gradle依赖将会是本文的重点。原本以前还想把artifact也讲解到,可是发现篇幅已经很长了,因此就一篇文章拆成两篇吧。android
Lifecycle的概念咱们在上一篇文章讲Project的时候也讲到过。在这篇文章中,咱们会再简单讲一讲做为引入。api
正如官网所说,gradle构建流程总共为三个阶段:bash
TaskExectionGraph
以便在执行阶段按照依赖关系执行Task。gradle taskname
进行执行。值得一提的是,咱们能够经过多种方式对project构建阶段进行监控和处理。如闭包
// 如下为Project的方法
afterEvaluate(closure),afterEvaluate(action)
beforeEvaluate(closure),beforeEvaluate(action)
// 如下为gradle提供的生命周期回调
afterProject(closure),afterProject(action)
beforeProject(closure),beforeProject(action)
buildFinished(closure),buildFinished(action)
projectsEvaluated(closure),projectsEvaluated(action)
projectsLoaded(closure),projectsLoaded(action)
settingsEvaluated(closure),settingsEvaluated(action)
addBuildListener(buildListener)
addListener(listener)
addProjectEvaluationListener(listener)
// Task也有这种方法
afterTask(Closure closure)
beforeTask(Closure closure)
//任务准备好后调用
whenReady(Closure closure)
复制代码
那么知道这样的构建流程咱们能够怎么使用呢?咱们能够进行监控,或者是动态的根据须要去控制project和task的构建执行。好比为了加快编译速度,咱们去掉一些测试的task。就能够这样写app
gradle.taskGraph.whenReady {
tasks.each { task ->
if (task.name.contains("Test")) {
task.enabled = false
} else if (task.name == "mockableAndroidJar") {
task.enabled = false
}
}
}
复制代码
Extension和Plugin都是咱们平常开发中常常有讲到的东西。上一篇文章中咱们讲到了build.gradle中的闭包都是有一个Delegate代理的,这个代理对象能够接受闭包中的参数传递给本身来使用,那么这个代理是啥呢?其实就是咱们这里要说的Extension。ide
1.Extension通俗来说其实就是一个普通的java bean。里面存放一些参数。使用须要借助于ExtensionContainer来进行建立。举个栗子工具
// 先定义一个实体类
public class Message {
String message = "empty"
String greeter = "none"
}
// 接下来在gradle文件中去使用project.extensions(也就是ExtensionContainer)来进行建立。
def extension = project.extensions.create("cyMessage", Message)
// 再写个task来进行验证
project.task('sendMessage') {
doLast {
println "${extension.message} from ${extension.greeter}"
println project.cyMessage.message + "from ${extension.greeter}"
}
}
复制代码
2.Plugin插件 讲完了Extension,咱们可能就会疑惑了。由于咱们在build.gradle中并无看到这些javabean和extension添加的操做啊,那么这些代码是在哪里写的呢?这时候咱们就要引入Plugin的概念了,也就是你看到的源码分析
apply plugin 'com.android.application'
复制代码
这个玩意了。这个就是Android Application的插件。android相关的Extension都是定义在这个插件中的。 有些同窗可能对插件不是很理解,为何须要这个东西。其实你们能够思考一下,gradle只是一个通用的构建工具。在他上层可能有各类应用,好比java,好比Android,甚至多是将来的鸿蒙。那么这些应用对gradle确定会有不一样的扩展,又确定不能把这些扩展直接放在gradle中。因此在上层添加插件就是个好选择,须要什么扩展就选什么插件。 Android开发中常见的插件有三种:
apply plugin 'com.android.application' // AppPlugin, 主module才能引用
apply plugin 'com.android.library' // LibraryPlugin, 普通的android module引用
apply plugin 'java' // java module的引用
复制代码
插件的其它知识不是本篇文章的重点,网上这方面的文章不少,你们能够自行学习。咱们根据如今掌握的知识就要去探索gradle更深层次的知识啦。
这两篇文章截止到如今,gradle构建相关的流程咱们基本都走通了。可是有个很重要的组件咱们没有去分析。就是依赖管理。咱们前一篇文章也讲到了,依赖管理是gradle的一个很重要的特性,方便咱们进行代码复用。那么咱们这一部分就专门讲一讲依赖管理究竟是怎么实现的。
首先咱们仍是看看基本的代码
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:appcompat-v7:26.1.0'
api 'com.android.support:appcompat-v7:26.1.0'
implementation project(':test')
androidTestImplementation 'com.android.support.test:runner:1.0.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0'
}
复制代码
咱们能够看到,通常有三种三方库类型,第一种是二进制文件,fileTree指向这个文件夹下的全部jar文件,第二种是三方库的string坐标形式,第三种是project的形式。 固然,咱们也会知道,代码中的implementation和api两种引用方式是有区别的。implement的依赖是不能传递的,可是api是能够的。
咱们在以前的研发中,可能没有去考虑深层次的这三种三方库类型,两种引用方式的缘由。当咱们按照上一篇中所说的代理模式去DependencyHandler中找implement, api等相关方法的时候,却发现,好像并无定义这些方法,那么这是怎么回事呢?若是没有定义,是否是能够随便定义一种引用方式呢?带着问题咱们往下面看
首先咱们仍是先介绍一下阅读gradle源码的方式。我尝试了不少种方式,发现仍是Android studio阅读起来最舒服,而后找到了一种方法。就是能够建一个gradle的demo,而后建一个module,把除了build.gradle之外的东西所有删掉。而后拷贝下面的代码进去。这样就能看到源码了
apply plugin 'java'
dependencies {
compile gradleApi()
compile 'xxxx' // 填入你gradle的版本
}
复制代码
首先咱们先介绍一个groovy语言的特性:methodmissing。你们能够参考官网 简单来讲就是当咱们预先在一个类中定义一个methodmissing方法。而后在这个类的对象上调用以前没有定义过的方法时,这个方法就会降级(fallback)到它所定义的methodmissing方法上。
class GORM {
def dynamicMethods = [...] // an array of dynamic methods that use regex
def methodMissing(String name, args) {
def method = dynamicMethods.find { it.match(name) }
if(method) {
GORM.metaClass."$name" = { Object[] varArgs ->
method.invoke(delegate, name, varArgs)
}
return method.invoke(delegate,name, args)
}
else throw new MissingMethodException(name, delegate, args)
}
}
assert new GORM().methodA(1) == resultA
复制代码
如图,当咱们调用methodA时,由于这个方法没有定义,就会转到methodmissing方法上,而且会把这个方法的名字methodA和它的参数一块儿传到methodmissing,这样若是dynamicMethod里面有定义methodA的话,这个方法就能执行了。这就是methodmissing的妙用。
为何须要这种机制呢?我理解这仍是为了扩展性。dependencies是gradle自身的功能。它不能彻底的总括全部上层应用可能会有的引用方式。每种插件均可能增长引用的方式,为了扩展性考虑,必须采用这种methodmissing的特性,把这些引用交给插件处理。好比Android的implement, api, annotationProcessor等。
在往下面讲解前,咱们先了解一下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.
也就是说,Configuration定义了依赖在编译和运行时候的不一样范围, 每一个Configuration都有name来区分。好比android常见的两种依赖方式implementation和api。
gradle中使用MethodMixIn这个接口来实现methodmissing的能力。
// MethodMixIn
public interface MethodMixIn {
MethodAccess getAdditionalMethods();
}
复制代码
public interface MethodAccess {
/** * Returns true when this object is known to have a method with the given name that accepts the given arguments. * * <p>Note that not every method is known. Some methods may require an attempt invoke it in order for them to be discovered.</p> */
boolean hasMethod(String name, Object... arguments);
/** * Invokes the method with the given name and arguments. */
DynamicInvokeResult tryInvokeMethod(String name, Object... arguments);
}
复制代码
能够看到这里的methodmissing主要是在找不到这个方法的时候去返回一个MethodAccess,MethodAccess中去判断是否存在以及动态执行这个method。
接下来咱们看DependencyHandler的实现类DefaultDependencyHandler。这个类实现了MethodMixIn接口,返回的是一个DynamicAddDependencyMethods对象。
// DefaultDependencyHandler.java
public DefaultDependencyHandler(...) {
...
this.dynamicMethods = new DynamicAddDependencyMethods(configurationContainer, new DefaultDependencyHandler.DirectDependencyAdder());
}
public MethodAccess getAdditionalMethods() {
return this.dynamicMethods;
}
复制代码
因此其实就是返回了一个DynamicAddDependencyMethods去加以判断。那么毫无疑问要在这个类中进行判断和执行具体方法。接下来咱们看看这个类中是怎么处理的。
DynamicAddDependencyMethods(ConfigurationContainer configurationContainer, DynamicAddDependencyMethods.DependencyAdder dependencyAdder) {
this.configurationContainer = configurationContainer;
this.dependencyAdder = dependencyAdder;
}
public boolean hasMethod(String name, Object... arguments) {
return arguments.length != 0 && this.configurationContainer.findByName(name) != null;
}
public DynamicInvokeResult tryInvokeMethod(String name, Object... arguments) {
if (arguments.length == 0) {
return DynamicInvokeResult.notFound();
} else {
Configuration configuration = (Configuration)this.configurationContainer.findByName(name);
if (configuration == null) {
return DynamicInvokeResult.notFound();
} else {
List<?> normalizedArgs = CollectionUtils.flattenCollections(arguments);
if (normalizedArgs.size() == 2 && normalizedArgs.get(1) instanceof Closure) {
return DynamicInvokeResult.found(this.dependencyAdder.add(configuration, normalizedArgs.get(0), (Closure)normalizedArgs.get(1)));
} else if (normalizedArgs.size() == 1) {
return DynamicInvokeResult.found(this.dependencyAdder.add(configuration, normalizedArgs.get(0), (Closure)null));
} else {
Iterator var5 = normalizedArgs.iterator();
while(var5.hasNext()) {
Object arg = var5.next();
this.dependencyAdder.add(configuration, arg, (Closure)null);
}
return DynamicInvokeResult.found();
}
}
}
}
复制代码
能够看到这个类的两个要点:
1.判断Configuration有无:经过外部传入的ConfigurationContainer来判断是否存在这个方法。这样咱们能够联想到,这个ConfigurationContainer确定是每一个平台Plugin本身传入的,必须是已定义的才能使用。好比android就添加了implementation, api等。若是你想查看Configuration在gradle源码中的初始化和配置,能够查看VariantDependencies这个类。
2.执行方法:真正的执行方法会根据参数来判断,好比咱们常见的一个参数的引用形式,还有一个参数+一个闭包的形式,好比
compile('com.zhyea:ar4j:1.0') {
exclude module: 'cglib' //by artifact name
}
复制代码
这种类型的引用。当在ConfigurationContainer中找到了这个引用方式(如下都称Configuration)时,就会返回一个DynamicInvokeResult。具体这个类的做用咱们后面再看,咱们先看他们都作了一个
this.dependencyAdder.add(configuration, arg, (Closure)null);
复制代码
的操做,这个操做是作了些什么呢,咱们继续往下跟就会发现,其实仍是调用了DefaultDependencyHandler的doAdd方法。
// DefaultDependencyHandler.java
private class DirectDependencyAdder implements DependencyAdder<Dependency> {
private DirectDependencyAdder() {
}
public Dependency add(Configuration configuration, Object dependencyNotation, @Nullable Closure configureAction) {
return DefaultDependencyHandler.this.doAdd(configuration, dependencyNotation, configureAction);
}
}
private Dependency doAdd(Configuration configuration, Object dependencyNotation, Closure configureClosure) {
if (dependencyNotation instanceof Configuration) {
Configuration other = (Configuration)dependencyNotation;
if (!this.configurationContainer.contains(other)) {
throw new UnsupportedOperationException("Currently you can only declare dependencies on configurations from the same project.");
} else {
configuration.extendsFrom(new Configuration[]{other});
return null;
}
} else {
Dependency dependency = this.create(dependencyNotation, configureClosure);
configuration.getDependencies().add(dependency);
return dependency;
}
}
复制代码
能够看到,这里会先判断dependencyNotation是不是Configuration,若是存在的话,就让当前的configuration继承dependencyNotation,也就是全部添加到dependencyNotation的依赖都会添加到configuration中。
这里可能有些朋友就会疑惑了,为啥还要对dependencyNotation判断呢?这个主要是为了处理嵌套的状况。好比implementation project(path: ':projectA', configuration: 'configA')这种类型的引用。有兴趣能够看看上面的CollectionUtils.flattenCollections(arguments)
方法。
总结一下,这个过程就是借助gradle的MethodMixIn接口,将全部未定义的引用方法转到getAdditionalMethods方法上来,在这个方法里面判断Configuration是否存在,若是存在的话就生成Dependency。
能够看到上面过程的最后,是DefaultDependencyHandler调用了create方法建立出了一个Dependency。咱们继续来分析建立Dependency的过程。
// DefaultDependencyHandler.java
public Dependency create(Object dependencyNotation, Closure configureClosure) {
Dependency dependency = this.dependencyFactory.createDependency(dependencyNotation);
return (Dependency)ConfigureUtil.configure(configureClosure, dependency);
}
// DefaultDependencyFactory.java
public Dependency createDependency(Object dependencyNotation) {
Dependency dependency = (Dependency)this.dependencyNotationParser.parseNotation(dependencyNotation);
this.injectServices(dependency);
return dependency;
}
复制代码
能够看到最终是调用了dependencyNotationParser来parse这个dependencyNotation。而这里的dependencyNotationParser其实就是DependencyNotationParser这个类。
public class DependencyNotationParser {
public static NotationParser<Object, Dependency> parser(Instantiator instantiator, DefaultProjectDependencyFactory dependencyFactory, ClassPathRegistry classPathRegistry, FileLookup fileLookup, RuntimeShadedJarFactory runtimeShadedJarFactory, CurrentGradleInstallation currentGradleInstallation, Interner<String> stringInterner) {
return NotationParserBuilder.toType(Dependency.class)
.fromCharSequence(new DependencyStringNotationConverter(instantiator, DefaultExternalModuleDependency.class, stringInterner))
.converter(new DependencyMapNotationConverter(instantiator, DefaultExternalModuleDependency.class))
.fromType(FileCollection.class, new DependencyFilesNotationConverter(instantiator))
.fromType(Project.class, new DependencyProjectNotationConverter(dependencyFactory))
.fromType(ClassPathNotation.class, new DependencyClassPathNotationConverter(instantiator, classPathRegistry, fileLookup.getFileResolver(), runtimeShadedJarFactory, currentGradleInstallation))
.invalidNotationMessage("Comprehensive documentation on dependency notations is available in DSL reference for DependencyHandler type.").toComposite();
}
}
复制代码
从里面咱们看到了FileCollection,Project,ClassPathNotation三个类,是否是感受和咱们的三种三方库资源形式很对应?其实这三种资源形式的解析就是用这三个类进行的。DependencyNotationParser就是整合了这些转换器,成为一个综合的转换器。其中,
这三种方式具体的解析方法你们能够自行阅读源码,不是本文重点。因此除了Project会被解析为ProjectDependency之外,其余的都是SelfResolvingDependency。其实ProjectDependency是SelfResolvingDependency的子类。他们的关系能够从SelfResolvingDependency的代码注释中看出。
接下来说讲ProjectDependency.一个常见的Project引用以下:
implementation project(‘:projectA’)
复制代码
这里的implementation咱们已经知道是插件添加的扩展,不是gradle自带的。那project呢?这个就是gradle自带的了。delegate是DependencyHandler的project方法。
// DefaultDependencyHandler.java
public Dependency project(Map<String, ?> notation) {
return this.dependencyFactory.createProjectDependencyFromMap(this.projectFinder, notation);
}
// DefaultDependencyFactory.java
public ProjectDependency createProjectDependencyFromMap(ProjectFinder projectFinder, Map<? extends String, ? extends Object> map) {
return this.projectDependencyFactory.createFromMap(projectFinder, map);
}
// ProjectDependencyFactory.java
public ProjectDependency createFromMap(ProjectFinder projectFinder, Map<? extends String, ?> map) {
return (ProjectDependency)NotationParserBuilder.toType(ProjectDependency.class).converter(new ProjectDependencyFactory.ProjectDependencyMapNotationConverter(projectFinder, this.factory)).toComposite().parseNotation(map);
}
// ProjectDependencyMapNotationConverter.java
protected ProjectDependency parseMap(@MapKey("path") String path, @Optional @MapKey("configuration") String configuration) {
return this.factory.create(this.projectFinder.getProject(path), configuration);
}
// DefaultProjectDependencyFactory.java
public ProjectDependency create(ProjectInternal project, String configuration) {
DefaultProjectDependency projectDependency = (DefaultProjectDependency)this.instantiator.newInstance(DefaultProjectDependency.class, new Object[]{project, configuration, this.projectAccessListener, this.buildProjectDependencies});
projectDependency.setAttributesFactory(this.attributesFactory);
projectDependency.setCapabilityNotationParser(this.capabilityNotationParser);
return projectDependency;
}
复制代码
咱们能够看到,传入的project最终传递给了ProjectDependencyMapNotationConverter。先去查找这个project,而后经过factory去create ProjectDependency对象,固然这里也有考虑到Configuration的影响, 最终是产生了一个DefaultProjectDependency。这就是ProjectDependency的产生过程。
看到这里,你们能够已经理解了不一样的依赖的解析方式,可是可能仍是不理解依赖究竟是一个什么东西。其实依赖库并非依赖三方库的源代码,而是依赖三方库的产物,产物又是经过一系列的Task执行产生的。也就是说,projectA依赖projectB,那么A就拥有了对于B的产物的全部权。关于产物咱们等后面再介绍。先了解一下Configuration对于产物有一些什么支持。 咱们看Configuration的代码, 能够发现他继承了FileCollection接口,而FileCollection又继承了Buildable接口。这个接口有啥用呢,用处大得很。先看官网介绍
Buildable表示着不少Task对象生成的产物。它里面只有一个方法。
public interface Buildable {
TaskDependency getBuildDependencies();
}
复制代码
咱们看看DefaultProjectDependency的实现。
// DefaultProjectDependency.java
public TaskDependencyInternal getBuildDependencies() {
return new DefaultProjectDependency.TaskDependencyImpl();
}
private class TaskDependencyImpl extends AbstractTaskDependency {
private TaskDependencyImpl() {
}
public void visitDependencies(TaskDependencyResolveContext context) {
if (DefaultProjectDependency.this.buildProjectDependencies) {
DefaultProjectDependency.this.projectAccessListener.beforeResolvingProjectDependency(DefaultProjectDependency.this.dependencyProject);
Configuration configuration = DefaultProjectDependency.this.findProjectConfiguration();
context.add(configuration);
context.add(configuration.getAllArtifacts());
}
}
}
public Configuration findProjectConfiguration() {
ConfigurationContainer dependencyConfigurations = this.getDependencyProject().getConfigurations();
String declaredConfiguration = this.getTargetConfiguration();
Configuration selectedConfiguration = dependencyConfigurations.getByName((String)GUtil.elvis(declaredConfiguration, "default"));
if (!selectedConfiguration.isCanBeConsumed()) {
throw new ConfigurationNotConsumableException(this.dependencyProject.getDisplayName(), selectedConfiguration.getName());
} else {
return selectedConfiguration;
}
}
复制代码
咱们能够看到,其实就是在解析每一个依赖的时候,若是指定了ConfigurationContainer中声明好的Configuration,好比implementation, api等,那就返回这个Configuration,不然就返回default。拿到这个Configuration以后,作了这个操做
context.add(configuration);
context.add(configuration.getAllArtifacts());
复制代码
这里的context是一个TaskDependencyResolveContext,它的add方法能够添加能contribute tasks to the result的对象,好比Task,TaskDependencies,Buildable等,这些类型都能为产生结果贡献Task(前面咱们说到了就是靠Task产生产物的嘛)。
这里context把configuration和configuration.getAllArtifacts()加入,都是做为Buildable而加入。区别是configuration.getAllArtifacts()获取的是DefaultPublishArtifactSet对象。接下来看看DefaultPublishArtifactSet是怎么实现Buildable的方法的。
public TaskDependency getBuildDependencies() {
return this.builtBy; // 这里的builtBy是下面的ArtifactsTaskDependency对象
}
private class ArtifactsTaskDependency extends AbstractTaskDependency {
private ArtifactsTaskDependency() {
}
public void visitDependencies(TaskDependencyResolveContext context) {
Iterator var2 = DefaultPublishArtifactSet.this.iterator();
while(var2.hasNext()) {
PublishArtifact publishArtifact = (PublishArtifact)var2.next();
context.add(publishArtifact);
}
}
}
复制代码
咱们发现果真和DefaultPublishArtifactSet的名字同样,是做为set把里面包含的PublishArtifact对象逐个的放入context中。
咱们在这一小节中分析了不一样的依赖方式的区别,告诉了你们Configuration是什么东西,也告诉了你们依赖究竟是怎么产生和起做用的。这样你们在平常开发中就更能知其因此然了。总结一下就是说
那么Task和产物又是一种什么关系呢?这个就涉及到更深层次了。本文篇幅有限,放在下一篇再分析吧。
本文主要是从Extension和Plugin引入,主要讲解了gradle依赖的原理和解析机制,而后抛下了一个疑问:产物artifacts是怎么和依赖扯上关系的呢?这个问题咱们等下一篇《一篇文章深刻gradle(下篇):Artifacts》再解答
gradle官网
我是Android笨鸟之旅,一个陪着你慢慢变强的公众号,欢迎关注我一块儿学习,一块儿进步哈~