Android Gradle 理解之Task与action

前言:

众所周知,一个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 doCreate(final String name, 
                                    final Class type, 
                                    final Object[] constructorArgs, 
                                    final Action configureAction) 
throws InvalidUserDataException {
        final TaskIdentity identity = TaskIdentity.create(name, type, this.project);
        return (Task)this.buildOperationExecutor.call(new CallableBuildOperation() {
            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 create(TaskIdentity taskIdentity, Object... args) {
        return this.process(this.taskFactory.create(taskIdentity, args));
}

public  S create(final TaskIdentity 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() {
                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 delete = new LinkedHashSet();

    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(){
            @Override
            public void execute(DeleteSpec deleteSpec) {
                deleteSpec.delete(paths).setFollowSymlinks(innerFollowSymLinks);
            }
        }).getDidWork());
    }
    ....
}        
 
 

可以看到此类最终也是继承defaultTask的,其中还有taskAction的注解,下面的篇章来分析下gradle如何去生成action对象的

回到刚刚的代码create里面

public  S create(TaskIdentity taskIdentity, Object... args) {
        return this.process(this.taskFactory.create(taskIdentity, args));
}

执行完taskFactory.create后,又执行了process方法

private  S process(S task) {
        TaskClassInfo taskClassInfo = this.taskClassInfoStore.getTaskClassInfo(task.getClass());
        if (taskClassInfo.isIncremental()) {
            task.getOutputs().upToDateWhen(new Spec() {
                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 type) {
        boolean cacheable = type.isAnnotationPresent(CacheableTask.class);
        boolean incremental = false;
        Map> processedMethods = Maps.newHashMap();
        Builder 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 taskType, Method method, Map> 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 process(S task) {
        TaskClassInfo taskClassInfo = this.taskClassInfoStore.getTaskClassInfo(task.getClass());
        if (taskClassInfo.isIncremental()) {
            task.getOutputs().upToDateWhen(new Spec() {
                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, TaskClassInfo> classInfos;


public TaskClassInfo getTaskClassInfo(Class type) {
        return (TaskClassInfo)this.classInfos.get(type, this.taskClassInfoFactory);
}

action的添加

这里可以很清晰的看到gradle拥有一个CrossBuildInMemoryCache的健值对,可以很清晰的看到拿到每个task的type对应的taskClassInfoTaskActionFactory去生成action对象,然后把action加到了List

 public void prependParallelSafeAction(Action 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  boolean doAdd(I toAdd, Action 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)
}

本质都是一样的

你可能感兴趣的:(Android Gradle 理解之Task与action)