前言
在往下面讲解前,我们先了解一下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.
单纯的翻译过来之后也是云里雾里的,不知所云。按照我的理解说一下。
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.2.0'
api 'org.gradle:gradle-plugins:0.9-rc-1'
}
implementation 与api 实际都是一个Configuration 类型的对象,这两个Configuration 对象是andorid 插件创建的。Configuration 可以下载关系各种依赖,同时andorid 插件 有规定了每一个Configuration 的作用范围。例如implementation 引入的依赖仅仅可以在当前project 使用,这也就是它的作用域。
一 查找方法
dependencies {
implementation 'androidx.appcompat:appcompat:1.2.0'
}
下面我们跟着源码看看这段代码是怎么工作的。
DefaultProject.java
@Override
public void dependencies(Closure configureClosure) {
ConfigureUtil.configure(configureClosure, getDependencies());
}
调用了ConfigureUtil.configure 方法
public static <T> T configure(@Nullable Closure configureClosure, T target) {
if (configureClosure == null) {
return target;
}
if (target instanceof Configurable) {
((Configurable) target).configure(configureClosure);
} else {
//走这里
configureTarget(configureClosure, target, new ConfigureDelegate(configureClosure, target));
}
return target;
}
target 这里为DependencyHandler,而DependencyHandler没有实现Configurable接口,因此这里都后面的分支。
这里有一个关键的类ConfigureDelegate,这个类可以看成是DependencyHandler的代理类,其会拦截方法的路由,我们稍后分析这个类。
private static <T> void configureTarget(Closure configureClosure, T target, ConfigureDelegate closureDelegate) {
//代码有删减。
Closure withNewOwner = configureClosure.rehydrate(target, closureDelegate, configureClosure.getThisObject());
//执行配置
new ClosureBackedAction<T>(withNewOwner, Closure.OWNER_ONLY, false).execute(target);
}
rehydrate 是配置Closure 内部调用的方法或属性的查找范围。
例如
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 会被当成一个方法,系统会在target, closureDelegate, configureClosure.getThisObject() 这三个对象里面查找是否存在这个方法,换句话说我们可以在Closure 闭包里面调用这三个对象的方法或者是属性。
查找顺序为Closure.OWNER_ONLY,表示优先在closureDelegate 里面查找。关于这一块大家可以查阅一下groovy 的Closure这一块,这里不在赘述。
我们这里着重分析ConfigureDelegate 类,这里这个类可以看成是DependencyHandler代理类。
public class ConfigureDelegate extends GroovyObjectSupport {
protected final DynamicObject _owner;
protected final DynamicObject _delegate;
private final ThreadLocal<Boolean> _configuring = new ThreadLocal<Boolean>() {
@Override
protected Boolean initialValue() {
return false;
}
};
public ConfigureDelegate(Closure configureClosure, Object delegate) {
_owner = DynamicObjectUtil.asDynamicObject(configureClosure.getOwner());
//_delegate 为BeanDynamicObject(delegate)
_delegate = DynamicObjectUtil.asDynamicObject(delegate);
}
@Override
public Object invokeMethod(String name, Object paramsObj) {
Object[] params = (Object[])paramsObj;
boolean isAlreadyConfiguring = _configuring.get();
_configuring.set(true);
try {
//通过_delegate的tryInvokeMethod 查找对应方法,注意这里是能找到一个方法的,我们放到后面介绍。
DynamicInvokeResult result = _delegate.tryInvokeMethod(name, params);
if (result.isFound()) {
return result.getValue();
}
MissingMethodException failure = null;
if (!isAlreadyConfiguring) {
try {
result = _configure(name, params);
} catch (MissingMethodException e) {
failure = e;
}
if (result.isFound()) {
return result.getValue();
}
}
// try the owner
result = _owner.tryInvokeMethod(name, params);
if (result.isFound()) {
return result.getValue();
}
if (failure != null) {
throw failure;
}
//抛出异常
throw _delegate.methodMissingException(name, params);
} finally {
_configuring.set(isAlreadyConfiguring);
}
}
}
由于ConfigureDelegate 没有implementation方法,根据前一章介绍的groovy方法路由过程,此时会调用ConfigureDelegate 的invokeMethod 方法,第一个参数是调用的方法名字也就是implementation,后面的参数是方法的参数,这里就是字符串’androidx.appcompat:appcompat:1.2.0’。
这里调用_delegate.tryInvokeMethod(name, params) 方法查找对应的方法,_delegate为BeanDynamicObject。
public DynamicInvokeResult invokeMethod(String name, Object... arguments) {
//通过MetaClass 在DependencyHandler 查找对应的方法
MetaClass metaClass = getMetaClass();
MetaMethod metaMethod = lookupMethod(metaClass, name, inferTypes(arguments));
if (metaMethod != null) {
return DynamicInvokeResult.found(metaMethod.doMethodInvoke(bean, arguments));
}
//如果没有找到对应的方法,判断是不是实现了MethodMixIn接口
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);
}
if (!implementsMissing) {
return DynamicInvokeResult.notFound();
}
return invokeOpaqueMethod(metaClass, name, arguments);
}
BeanDynamicObject 这个了我们这里前一章已经介绍过了。
BeanDynamicObject 会首先通过MetaClass 在被代理类(此处就是DependencyHandler)里面查找对应的方法是否存在,由于DependencyHandler 并没有implementation这样一个方法,因此后面会判断DependencyHandler 是否实现了MethodMixIn接口,如果是,则调用getAdditionalMethods 的tryInvokeMethod 查找对应的方法。这里DependencyHandler的实际类型是DefaultDependencyHandler而DefaultDependencyHandler 实现了MethodMixIn接口
下面我们看看DefaultDependencyHandler 的
private final DynamicMethods dynamicMethods;
@Override
public MethodAccess getAdditionalMethods() {
return dynamicMethods;
}
DynamicMethods 是DefaultDependencyHandler 的一个内部类。
@Override
public DynamicInvokeResult tryInvokeMethod(String name, Object... arguments) {
//判断参数的个数
if (arguments.length == 0) {
return DynamicInvokeResult.notFound();
}
//查找对应的Configuration
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) {
//执行doAdd 方法
return DynamicInvokeResult.found(doAdd(configuration, normalizedArgs.get(0), (Closure) normalizedArgs.get(1)));
} else if (normalizedArgs.size() == 1) {
return DynamicInvokeResult.found(doAdd(configuration, normalizedArgs.get(0), null));
} else {
for (Object arg : normalizedArgs) {
doAdd(configuration, arg, null);
}
return DynamicInvokeResult.found();
}
}
DynamicMethods 会根据调用的方法名字查找是否存在对应的Configuration ,如果存在那么调用功能Configuration 的doAdd 方法。
因此表面山我们是想调用一个名为implementation 的方法,但是最终却是获取一个名为implementation 的Configuration 对象,然后调用Configuration 的doAdd 方法。
示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。
二、添加依赖
dependencies {
implementation project(":testandroid")
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.2.0'
}
我们可以看到,一般有三种三方库类型,
第一种是二进制文件,fileTree指向这个文件夹下的所有jar文件,
第二种是三方库的string坐标形式,
第三种是project的形式。
当查找到对应的 Configuration 之后就会调用doAdd 方法添加依赖。
private Dependency doAdd(Configuration configuration, Object dependencyNotation, Closure configureClosure) {
if (dependencyNotation instanceof Configuration) {
Configuration other = (Configuration) dependencyNotation;
if (!configurationContainer.contains(other)) {
throw new UnsupportedOperationException("Currently you can only declare dependencies on configurations from the same project.");
}
configuration.extendsFrom(other);
return null;
}
//
Dependency dependency = create(dependencyNotation, configureClosure);
configuration.getDependencies().add(dependency);
return dependency;
}
参数dependencyNotation 表示的就是implementation 后面的参数。
可以看到,这里会先判断dependencyNotation是否是Configuration,如果存在的话,就让当前的configuration继承dependencyNotation,也就是所有添加到dependencyNotation的依赖都会添加到configuration中。
这里可能有些朋友就会疑惑了,为啥还要对dependencyNotation判断呢?这个主要是为了处理嵌套的情况。比如implementation project(path: ‘:projectA’, configuration: ‘configA’)这种类型的引用。有兴趣可以看看上面的CollectionUtils.flattenCollections(arguments)方法。这里不考虑这种情形。
之后调用create 方法创建对应的依赖,我们看看创建的过程
2.2 创建依赖
@Override
public Dependency create(Object dependencyNotation, Closure configureClosure) {
Dependency dependency = dependencyFactory.createDependency(dependencyNotation);
return ConfigureUtil.configure(configureClosure, dependency);
}
dependencyFactory 的实际类型是DefaultDependencyFactory
DefaultDependencyFactory.java
public Dependency createDependency(Object dependencyNotation) {
return dependencyNotationParser.parseNotation(dependencyNotation);
}
可以看到最终是调用了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) {
return NotationParserBuilder
.toType(Dependency.class)
.fromCharSequence(new DependencyStringNotationConverter<DefaultExternalModuleDependency>(instantiator, DefaultExternalModuleDependency.class))
.converter(new DependencyMapNotationConverter<DefaultExternalModuleDependency>(instantiator, DefaultExternalModuleDependency.class))
.fromType(FileCollection.class, new DependencyFilesNotationConverter(instantiator))
.fromType(Project.class, new DependencyProjectNotationConverter(dependencyFactory))
.fromType(DependencyFactory.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();
}
}
其中DependencyStringNotationConverter 用于 将 ‘androidx.appcompat:appcompat:1.2.0’ 这种形式的依赖转换为DefaultExternalModuleDependency。
DependencyFilesNotationConverter 用于将fileTree(dir: ‘libs’, include: [’*.jar’]) 这种形式的依赖转换为DefaultProjectDependency。
DependencyClassPathNotationConverter 用于将project(":testandroid") 这种形式的依赖转换为DefaultSelfResolvingDependency。
DependencyClassPathNotationConverter 用于将gradleApi 解析为对应的依赖。
dependencies {
gradleApi()
localGroovy()
}
我们这里以DependencyProjectNotationConverter 为例子
public class DependencyProjectNotationConverter implements NotationConverter<Project, ProjectDependency> {
private final DefaultProjectDependencyFactory factory;
public DependencyProjectNotationConverter(DefaultProjectDependencyFactory factory) {
this.factory = factory;
}
public void convert(Project notation, NotationConvertResult<? super ProjectDependency> result) throws TypeConversionException {
result.converted(factory.create(notation));
}
}
这里调用工厂类的创建了一个ProjectDependency,关于这个工厂是怎么工作的这里不在继续深入。
2.3 依赖的实质
看到这里,大家可以已经理解了不同的依赖的解析方式,但是到了这里还没有完,因为依赖最终是依赖的产物,例如依赖一个android module 最终依赖的是DefaultProjectDependency 的产物,这个产物是衣aar 文件,依赖一个java module 依赖的也是DefaultProjectDependency 的产物,但是这个产物是jar文件。也就是最终的产物与具体的平台有关系。所有的产物都是通过Task 完成。
public TaskDependencyInternal getBuildDependencies() {
return new TaskDependencyImpl();
}
getBuildDependencies 是Buildable 接口定义的方法。
Buildable接口。这个接口有啥用呢,用处大得很。先看官网介绍
Buildable表示很多Task对象生成的产物。它里面只有一个方法。
public interface Buildable {
TaskDependency getBuildDependencies();
DefaultProjectDependency 的getBuildDependencies 返回的是 TaskDependencyImpl,通过这个TaskDependencyImpl我们可以获取到生产产物对应的Task集合
实验
dependencies {
implementation project(":testandroid")
}
task testBuild{
}
project.configurations.getByName("implementation").getDependencies().forEach{
it->
if (it instanceof ProjectDependency){
def p=(ProjectDependency) it
p.buildDependencies.getDependencies(tasks.findByName("testBuild")).forEach{
taskName->
def taskName2=(Task) taskName
println(" Task name is ${taskName2.toString()}")
println(" Task name des is ${taskName2.description}")
taskName2.outputs.getFiles().forEach{fileItem->
//输出产物的名字
println(" File name des is ${fileItem.getName()}")
}
}
}
}
执行 gradlew testBuild 命令
如上我们通过一个例子获取到了DefaultProjectDependency 生产产物的Task 以及最终的产物Aar 文件。
TDefaultProjectDependency 的getBuildDependencies 返回的是askDependencyImpl
private class TaskDependencyImpl extends AbstractTaskDependency {
@Override
public void visitDependencies(TaskDependencyResolveContext context) {
if (!buildProjectDependencies) {
return;
}
projectAccessListener.beforeResolvingProjectDependency(dependencyProject);
Configuration configuration = findProjectConfiguration();
context.add(configuration);
context.add(configuration.getAllArtifacts());
}
}
public abstract class AbstractTaskDependency implements TaskDependencyInternal {
public Set<Task> getDependencies(Task task) {
CachingTaskDependencyResolveContext context = new CachingTaskDependencyResolveContext();
context.add(this);
return context.resolve(task);
}
}
public void add(Object dependency) {
Preconditions.checkNotNull(dependency);
queue.add(dependency);
}
CachingTaskDependencyResolveContext 可以看成是一个列表。这里将自己加入到列表内部,然后调用resolve 方法
public Set<Task> resolve(Task task) {
this.task = task;
try {
return doResolve();
} catch (Exception e) {
throw new TaskDependencyResolveException(String.format("Could not determine the dependencies of %s.", task), e);
} finally {
queue.clear();
this.task = null;
}
}
private Set<Task> doResolve() {
walker.add(queue);
return walker.findValues();
}
注意此时此时queue 里面仅仅存在一个元素也即是TaskDependencyImpl
public Set<T> findValues() {
try {
return doSearch();
} finally {
startNodes.clear();
}
}
TaskDependencyImpl 的getDependencies 中
private Set<T> doSearch() {
int componentCount = 0;
Map<N, NodeDetails<N, T>> seenNodes = new HashMap<N, NodeDetails<N, T>>();
Map<Integer, NodeDetails<N, T>> components = new HashMap<Integer, NodeDetails<N, T>>();
LinkedList<N> queue = new LinkedList<N>(startNodes);
while (!queue.isEmpty()) {
N node = queue.getFirst();
NodeDetails<N, T> details = seenNodes.get(node);
if (details == null) {
// Have not visited this node yet. Push its successors onto the queue in front of this node and visit
// them
details = new NodeDetails<N, T>(node, componentCount++);
seenNodes.put(node, details);
components.put(details.component, details);
Set<T> cacheValues = cachedNodeValues.get(node);
if (cacheValues != null) {
// Already visited this node
details.values = cacheValues;
details.finished = true;
queue.removeFirst();
continue;
}
//这是关键
graph.getNodeValues(node, details.values, details.successors);
for (N connectedNode : details.successors) {
NodeDetails<N, T> connectedNodeDetails = seenNodes.get(connectedNode);
if (connectedNodeDetails == null) {
// Have not visited the successor node, so add to the queue for visiting
queue.add(0, connectedNode);
} else if (!connectedNodeDetails.finished) {
// Currently visiting the successor node - we're in a cycle
details.stronglyConnected = true;
}
// Else, already visited
}
} else {
// Have visited all of this node's successors
queue.removeFirst();
if (cachedNodeValues.containsKey(node)) {
continue;
}
for (N connectedNode : details.successors) {
NodeDetails<N, T> connectedNodeDetails = seenNodes.get(connectedNode);
if (!connectedNodeDetails.finished) {
// part of a cycle : use the 'minimum' component as the root of the cycle
int minSeen = Math.min(details.minSeen, connectedNodeDetails.minSeen);
details.minSeen = minSeen;
connectedNodeDetails.minSeen = minSeen;
details.stronglyConnected = true;
}
details.values.addAll(connectedNodeDetails.values);
graph.getEdgeValues(node, connectedNode, details.values);
}
if (details.minSeen != details.component) {
// Part of a strongly connected component (ie cycle) - move values to root of the component
// The root is the first node of the component we encountered
NodeDetails<N, T> rootDetails = components.get(details.minSeen);
rootDetails.values.addAll(details.values);
details.values.clear();
rootDetails.componentMembers.addAll(details.componentMembers);
} else {
// Not part of a strongly connected component or the root of a strongly connected component
for (NodeDetails<N, T> componentMember : details.componentMembers) {
cachedNodeValues.put(componentMember.node, details.values);
componentMember.finished = true;
components.remove(componentMember.component);
}
if (details.stronglyConnected) {
strongComponents.add(details);
}
}
}
}
Set<T> values = new LinkedHashSet<T>();
for (N startNode : startNodes) {
values.addAll(cachedNodeValues.get(startNode));
}
return values;
}
doSearch 实际是对整个依赖树的处理,例如projectA 依赖projectB ,同时projectB 由依赖projectC。整个doSearch 的过程正是对这个树的遍历过程,queue 在遍历的过程中起到辅助作用,有不了解遍历算法的鹅同学可以百度一个树的非递归遍历算法。
private class TaskGraphImpl implements DirectedGraph<Object, Task> {
public void getNodeValues(Object node, Collection<? super Task> values, Collection<? super Object> connectedNodes) {
if (node instanceof TaskDependencyContainer) {
TaskDependencyContainer taskDependency = (TaskDependencyContainer) node;
//这个queue 跟上面那个queue 是同一个对象。
queue.clear();
//添加元素
taskDependency.visitDependencies(CachingTaskDependencyResolveContext.this);
connectedNodes.addAll(queue);
} else if (node instanceof Buildable) {
//
Buildable buildable = (Buildable) node;
connectedNodes.add(buildable.getBuildDependencies());
} else if (node instanceof TaskDependency) {
TaskDependency dependency = (TaskDependency) node;
values.addAll(dependency.getDependencies(task));
} else if (node instanceof Task) {
values.add((Task) node);
} else if (node instanceof TaskReference) {
TaskContainerInternal tasks = (TaskContainerInternal) task.getProject().getTasks();
Task task = tasks.resolveTask((TaskReference) node);
values.add(task);
} else {
throw new IllegalArgumentException(String.format("Cannot resolve object of unknown type %s to a Task.",
node.getClass().getSimpleName()));
}
}
}
初始的时候queue 里面仅有一个元素就是TaskDependencyImpl,而TaskDependencyImpl 实现了TaskDependencyContainer 接口,因此会执行第一个分支。
public void visitDependencies(TaskDependencyResolveContext context) {
if (!buildProjectDependencies) {
return;
}
projectAccessListener.beforeResolvingProjectDependency(dependencyProject);
Configuration configuration = findProjectConfiguration();
//添加Configuration
context.add(configuration);
//getAllArtifacts 返回PublishArtifactSet,其实现了Buildable
context.add(configuration.getAllArtifacts());
}
一
以 implementation project(":testandroid") 为例子,findProjectConfiguration 返回的实际是testandroid 这个module 的名为default的Configuration 。每一个Configuration 都有自己的产物。Configuration 也实现了Buildable接口。
执行完visitDependencies 之后此时queue多了两个元素,这两个元素在后续也会被遍历,进而执行getNodeValues,遍历到Configuration 的时候会进入到第二个分支,configuration 的getBuildDependencies 返回的是
TaskDependency,后面接着循环的时候会接着掉用器getNodeValues 处理TaskDependency,
对于TaskDependency 会调用getDependencies,getDependencies 返回一系列的task ,而这些task 正是p.getBuildDependencies().getDependencies(task)的返回值,测试发现返回的Task 列表为空也就是此时还没有找到输出产物的Task
二
configuration.getAllArtifacts() 返回值PublishArtifactSet 实现了Buildable 接口,测试验证最终输出产物的Task 就包含在这里面。
到此我们知道了通过configuration.getAllArtifacts()可以获取到将源码编译打包成jar的Task,对于Java modue 而言这个Task为jar,
archives 是JavaPlugin 定义的一个Configuration。
对于andorid 而言这个Task 是BundleAar
LibraryTaskManager.java
private void createBundleTask(@NonNull VariantScope variantScope) {
TaskProvider<BundleAar> bundle =
taskFactory.register(new BundleAar.CreationAction(variantScope));
xxxx
project.getArtifacts().add("default", bundle);
}
}