前言:
众所周知,一个build.gradle代表着一个project,每个project都有若干个task和每个project里的属性和额外属性还有方法,下面这篇文章主要讲述gradle中task相关的原理,要学习gradle个人脑海里必须要觉得build.gradle中的配置是groovy语言,并不是简单的脚本,下面来用最简单的例子阐述下task:
通过此篇文章,你将了解如下知识点:
- task对象的创建以及其重要的参数type带来的影响
- task中的action如何被实例化,以及其和task的关系
- task中闭包和action的本质
- 如何在插入指定task到系统的task中间
声明一个简单的task
task myTask {
println "do task"
}
下面将用这个简单的例子来看下task的生成的原理
task的生成
可以看到上述是一个很简单的task,既然我说把gradle的配置当语言来看,那为何没有对象.方法名
其实这个个人理解类似于kotlin的infix函数,看下面的例子
infix fun Int.add(x: Int): Int {
return this + x
}
println(100 add 200)
我们在看下原来的task,其实写法等同于
task("myTask") {
println "do task"
}
那还有一个问题是谁调用了task方法呢?
个人理解就是它会去找调用你task的所在的project,比如我在app的build.gradle中新建task,那他就会自动找到app的build.gradle里的project去调用其task方法,等同于如下写法
project.tasks.create("myTask").configure {
println "do task"
}
但无论哪种写法,最终都会执行到DefaultTaskContainer类的doCreate方法
private <T extends Task> T doCreate(final String name,
final Class<T> type,
final Object[] constructorArgs,
final Action<? super T> configureAction)
throws InvalidUserDataException {
final TaskIdentity<T> identity = TaskIdentity.create(name, type, this.project);
return (Task)this.buildOperationExecutor.call(new CallableBuildOperation<T>() {
public T call(BuildOperationContext context) {
try {
//在此创建task
T task = DefaultTaskContainer.this.createTask(identity, constructorArgs);
DefaultTaskContainer.this.statistics.eagerTask(type);
DefaultTaskContainer.this.addTask(task, false);
configureAction.execute(task);
context.setResult(DefaultTaskContainer.REALIZE_RESULT);
return task;
} catch (Throwable var3) {
throw DefaultTaskContainer.this.taskCreationException(name, var3);
}
}
public Builder description() {
return DefaultTaskContainer.realizeDescriptor(identity, false, true);
}
});
}
这里简单说下,首先会把name,type和此构造方法的参数合并成TaskIdentity对象,然后由DefaultTaskContainer去创建
最终会执行到这个代理方法中
public <S extends Task> S create(TaskIdentity<S> taskIdentity, Object... args) {
return this.process(this.taskFactory.create(taskIdentity, args));
}
public <S extends Task> S create(final TaskIdentity<S> identity, final Object... args) {
if (!Task.class.isAssignableFrom(identity.type)) {
throw new InvalidUserDataException(String.format("Cannot create task of type '%s' as it does not implement the Task interface.", identity.type.getSimpleName()));
} else {
NameValidator.validate(identity.name, "task name", "");
final Class implType;
if (identity.type.isAssignableFrom(DefaultTask.class)) {
implType = DefaultTask.class;
} else {
implType = identity.type;
}
return AbstractTask.injectIntoNewInstance(this.project, identity, new Callable<S>() {
public S call() {
try {
return (Task)identity.type.cast(TaskFactory.this.instantiator.newInstance(implType, args));
} catch (ObjectInstantiationException var2) {
throw new TaskInstantiationException(String.format("Could not create task of type '%s'.", identity.type.getSimpleName()), var2.getCause());
}
}
});
}
}
上述代码TaskFactory会通过反射去创建一个DefaultTask类型的task对象,名字是"myTask1"的task
声明一个带type的task
接下来看下另外一种比较常见但是让别人疑惑的task,借用以下例子主要分析下action的生成,以及action的添加,task添加的原理
task clean(type: Delete) {
delete rootProject.buildDir
}
这代码相信很多人都看到过,一般在rootProject中会写如下代码:
以前一直疑惑type是干嘛用的,其实本质就是通过反射去生层一个Delete类型的task对象,然后task的名字叫"clean",就这么简单,具体代码在上文已经简述了,这里不再多说
很多文章说type是clean task的子类型或者是包含,看过分析后完全不是那回事,clean只是此task的名字,根本不存在包含或者子类型
action的生成
在创建task之后,紧接着当然是task中的action了,我们来看下我们刚刚声明的带type的task里面的Delete类
public class Delete extends ConventionTask implements DeleteSpec {
private Set<Object> delete = new LinkedHashSet<Object>();
private boolean followSymlinks;
@Inject
protected FileSystem getFileSystem() {
// Decoration takes care of the implementation
throw new UnsupportedOperationException();
}
@Inject
protected FileResolver getFileResolver() {
// Decoration takes care of the implementation
throw new UnsupportedOperationException();
}
/**
* Injected Clock.
*
* @since 5.3
*/
@Inject
protected Clock getClock() {
// Decoration takes care of the implementation
throw new UnsupportedOperationException();
}
@TaskAction
protected void clean() {
Deleter deleter = new Deleter(getFileResolver(), getFileSystem(), getClock());
final boolean innerFollowSymLinks = followSymlinks;
final Object[] paths = delete.toArray();
setDidWork(deleter.delete(new Action<DeleteSpec>(){
@Override
public void execute(DeleteSpec deleteSpec) {
deleteSpec.delete(paths).setFollowSymlinks(innerFollowSymLinks);
}
}).getDidWork());
}
....
}
可以看到此类最终也是继承defaultTask的,其中还有taskAction的注解,下面的篇章来分析下gradle如何去生成action对象的
回到刚刚的代码create里面
public <S extends Task> S create(TaskIdentity<S> taskIdentity, Object... args) {
return this.process(this.taskFactory.create(taskIdentity, args));
}
执行完taskFactory.create后,又执行了process方法
private <S extends Task> S process(S task) {
TaskClassInfo taskClassInfo = this.taskClassInfoStore.getTaskClassInfo(task.getClass());
if (taskClassInfo.isIncremental()) {
task.getOutputs().upToDateWhen(new Spec<Task>() {
public boolean isSatisfiedBy(Task element) {
return true;
}
});
}
UnmodifiableIterator var3 = taskClassInfo.getTaskActionFactories().iterator();
while(var3.hasNext()) {
TaskActionFactory actionFactory = (TaskActionFactory)var3.next();
((TaskInternal)task).prependParallelSafeAction(actionFactory.create(this.instantiator));
}
if (taskClassInfo.isCacheable()) {
task.getOutputs().cacheIf("Annotated with @CacheableTask", Specs.SATISFIES_ALL);
}
}
重点看下getTaskClassInfo方法,此方法最终会去创建Action,不信你看!!
private TaskClassInfo createTaskClassInfo(Class<? extends Task> type) {
boolean cacheable = type.isAnnotationPresent(CacheableTask.class);
boolean incremental = false;
Map<String, Class<?>> processedMethods = Maps.newHashMap();
Builder<TaskActionFactory> taskActionFactoriesBuilder = ImmutableList.builder();
for(Class current = type; current != null; current = current.getSuperclass()) {
Method[] var7 = current.getDeclaredMethods();
int var8 = var7.length;
for(int var9 = 0; var9 < var8; ++var9) {
Method method = var7[var9];
TaskActionFactory taskActionFactory = createTaskAction(type, method, processedMethods);
if (taskActionFactory != null) {
if (taskActionFactory instanceof DefaultTaskClassInfoStore.AbstractIncrementalTaskActionFactory) {
if (incremental) {
throw new GradleException(String.format("Cannot have multiple @TaskAction methods accepting an %s or %s parameter.", InputChanges.class.getSimpleName(), IncrementalTaskInputs.class.getSimpleName()));
}
incremental = true;
}
taskActionFactoriesBuilder.add(taskActionFactory);
}
}
}
return new TaskClassInfo(incremental, taskActionFactoriesBuilder.build(), cacheable);
}
代码很明显了把,简单来说就是扫描此task类和他的父类,然后去找到taskAction
创建taskAction的代码如下
private static TaskActionFactory createTaskAction(Class<? extends Task> taskType, Method method, Map<String, Class<?>> processedMethods) {
if (method.getAnnotation(TaskAction.class) == null) {
return null;
} else {
Class<?> declaringClass = method.getDeclaringClass();
if (Modifier.isStatic(method.getModifiers())) {
throw new GradleException(String.format("Cannot use @TaskAction annotation on static method %s.%s().", declaringClass.getSimpleName(), method.getName()));
} else {
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length > 1) {
throw new GradleException(String.format("Cannot use @TaskAction annotation on method %s.%s() as this method takes multiple parameters.", declaringClass.getSimpleName(), method.getName()));
} else {
Object taskActionFactory;
Class parameterType;
if (parameterTypes.length == 1) {
parameterType = parameterTypes[0];
if (parameterType.equals(IncrementalTaskInputs.class)) {
taskActionFactory = new DefaultTaskClassInfoStore.IncrementalTaskInputsTaskActionFactory(taskType, method);
} else {
if (!parameterType.equals(InputChanges.class)) {
throw new GradleException(String.format("Cannot use @TaskAction annotation on method %s.%s() because %s is not a valid parameter to an action method.", declaringClass.getSimpleName(), method.getName(), parameterType));
}
taskActionFactory = new DefaultTaskClassInfoStore.IncrementalInputsTaskActionFactory(taskType, method);
}
} else {
taskActionFactory = new DefaultTaskClassInfoStore.StandardTaskActionFactory(taskType, method);
}
parameterType = (Class)processedMethods.put(method.getName(), declaringClass);
if (parameterType == declaringClass) {
throw new GradleException(String.format("Cannot use @TaskAction annotation on multiple overloads of method %s.%s()", declaringClass.getSimpleName(), method.getName()));
} else {
return (TaskActionFactory)(parameterType != null ? null : taskActionFactory);
}
}
}
}
}
嫣然回首,发现这全部是熟悉的找注解的方法啊,可以看到找到@TaskAction注解,然后用StandardTaskActionFactory生成taskActionFactory最后返回到createTaskClassInfo方法里,最后在返回createTaskClassInfo对象,也就是说其对象中有对应的task的action的方法名,再来看下process方法
private <S extends Task> S process(S task) {
TaskClassInfo taskClassInfo = this.taskClassInfoStore.getTaskClassInfo(task.getClass());
if (taskClassInfo.isIncremental()) {
task.getOutputs().upToDateWhen(new Spec<Task>() {
public boolean isSatisfiedBy(Task element) {
return true;
}
});
}
UnmodifiableIterator var3 = taskClassInfo.getTaskActionFactories().iterator();
while(var3.hasNext()) {
TaskActionFactory actionFactory = (TaskActionFactory)var3.next();
((TaskInternal)task).prependParallelSafeAction(actionFactory.create(this.instantiator));
}
if (taskClassInfo.isCacheable()) {
task.getOutputs().cacheIf("Annotated with @CacheableTask", Specs.SATISFIES_ALL);
}
return task;
}
private final CrossBuildInMemoryCache<Class<?>, TaskClassInfo> classInfos;
public TaskClassInfo getTaskClassInfo(Class<? extends Task> type) {
return (TaskClassInfo)this.classInfos.get(type, this.taskClassInfoFactory);
}
action的添加
这里可以很清晰的看到gradle拥有一个CrossBuildInMemoryCache的健值对,可以很清晰的看到拿到每个task的type对应的taskClassInfoTaskActionFactory去生成action对象,然后把action加到了List<InputChangesAwareTaskAction>中
public void prependParallelSafeAction(Action<? super Task> action) {
if (action == null) {
throw new InvalidUserDataException("Action must not be null!");
} else {
this.getTaskActions().add(0, this.wrap(action));
}
}
这里可以很明显的看到gradle有个开关,分别是cache和up to date
最终会调用defaultTaskContainer的addInternal方法和isIncremental 代表支持缓存和增量编译
最后来看下task是如何被添加的,还记得上文的doCreate方法么?
task的添加
在生成了task后自然调用DefaultTaskContainer的addInternal方法
public boolean addInternal(Task task) {
return super.add(task);
}
继而调用DefaultDomainObjectCollection的add方法
protected <I extends T> boolean doAdd(I toAdd, Action<? super I> notification) { if (this.getStore().add(toAdd)) { this.didAdd(toAdd); notification.execute(toAdd); return true; } else { return false; } }
也就存在了ElementSource接口的对象中,最后直接回调闭包中的内容,其实闭包的所调用的方法就是configure方法
注意这里的this,就是要回调的的参数类型,很明显此处就是Delete类型的task对象
这里最终会调用ClosureBackedAction的execute方法
if (this.closure != null) {
try {
if (this.configurableAware && delegate instanceof Configurable) {
((Configurable)delegate).configure(this.closure);
} else {
Closure copy = (Closure)this.closure.clone();
copy.setResolveStrategy(this.resolveStrategy);
copy.setDelegate(delegate);
if (copy.getMaximumNumberOfParameters() == 0) {
copy.call();
} else {
copy.call(delegate);
}
}
} catch (MissingMethodException var3) {
if (Objects.equal(var3.getType(), this.closure.getClass()) && Objects.equal(var3.getMethod(), "doCall")) {
throw new InvalidActionClosureException(this.closure, delegate);
} else {
throw var3;
}
}
}
}
最终回调给闭包里的对象,所以也就是说闭包里面的代码是立即执行的,也就是在配置阶段执行的原因
如何插入task在指定task中间
在日常开发中,你可能需要插入task在指定task中间,其实知道原理了很好实现,思想就是在配置阶段结束后也就是生成task的环向图之前做,具体代码如下
def insertTask = project.task("clean", type: Delete).doFirst {
println("cleanAction")
}
project.afterEvaluate {
compileDebugKotlin.dependsOn(insertTask)
insertTask.mustRunAfter(processDebugResources)
}
这样自己的clean的task就加在了processDebugResources的task之后,compileDebugKotlin的task之前了
当然也可以这么写
def insertTask = project.task("clean", type: Delete).doFirst {
println("cleanAction")
}
project.afterEvaluate {
def task = getTasks().getByName("compileDebugKotlin")
task.dependsOn(insertTask)
insertTask.mustRunAfter(processDebugResources)
}
本质都是一样的
网友评论