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笨鳥之旅,一個陪着你慢慢變強的公衆號,歡迎關注我一塊兒學習,一塊兒進步哈~