Android 自定义Task添加到任务队列

Posted 好人静

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 自定义Task添加到任务队列相关的知识,希望对你有一定的参考价值。

前言

         逐步整理的一系列的总结:

        Android Gradle插件开发初次交手(一)

        Android Gradle的基本概念梳理(二)

        Android 自定义Gradle插件的完整流程(三)

       android 自定义Task添加到任务队列(四)

       Android 自定义Gradle插件的多层属性扩展(五)

      Android Gradle 中的Transform(六)

      Android Gradle之Java字节码(七)

       Android Gradle 中的字节码插桩之ASM(八)

      Android Gradle 中的使用ASMified插件生成.class的技巧(九)

      Android Gradle 中的实例之动态修改AndroidManifest文件(十)


        在Android 自定义Gradle插件的完整流程(三)3.Task中的属性扩展部分主要介绍的是将自定义插件中的自定义Task加入到被依赖module(即通过apply或者plugins添加该插件的那个module),其实我又仔细的想了想,为什么不直接把自定义的Task直接加入到当前插件的任务队列中呢?

一 关系梳理

           在Android 自定义Gradle插件的完整流程(三)3.Task中的属性扩展的第(3)步中主要在被依赖module执行apply或者plugins来应用该插件的时候,会执行到自定义插件的入口类的apply(project)的方法,我们此时将我们自定义的HandleTemplateTask添加到了依赖module的任务队列中,在编译阶段相应的输出内容如下:

//开始配置app这个module
> Configure project :app
<<<<<<<settings.gradle<<<<<<<<<<    beforeProject Project name = app
//app 执行plugins,调用到该自定义插件的apply()方法
%%%%%%%%% FirstPluginProject %%%%%%%%% Apply the First Plugin Project
// app module已经完成配置
@@@@@@@@@@@@@@ app @@@@@@@@@@@@@   afterEvaluate
//开始配置自定义插件:即完成将插件的build.gradle的配置以及task的依赖关系图
> Configure project :firstplugin
<<<<<<<settings.gradle<<<<<<<<<<    beforeProject Project name = firstplugin
//完成自定义插件工程的配置阶段
<<<<<<<settings.gradle<<<<<<<<<<     projectsEvaluated 已经完成Gradle构建的配置阶段
//执行自定义插件的所有Tasks
> Task :firstplugin:compileJava NO-SOURCE
.........
//该自定义Task放到app的任务队列中执行
> Task :app:handleTemplateTask
%%%%%%%%% FirstPluginProject %%%%%%%%%  HandleTemplateTask is running 
%%%%%%%%% FirstPluginProject %%%%%%%%%  Set the file format is " .java
%%%%%%%%% FirstPluginProject %%%%%%%%%  Set the file source dir is " /Users/j1/Documents/android/code/AndroidPlugin/app/src/main/java/mvp
> Task :app:preBuild
> Task :app:preDebugBuild
.........

        从这个任务输出信息中可以看到:

  • (1)首先开始配置的是被依赖module(此实例为app这个module),即 “> Configure project :app”信息输出;
  • (2)在配置被依赖module(此实例为app这个module)的时候,会首先调用到自定义插件(此实例为firstplugin这个module)的入口类的apply(project)的方法,即“%%%%%%%%% FirstPluginProject %%%%%%%%% Apply the First Plugin Project”信息的输出;
  • (3)然后完成整个被依赖module(此实例为app这个module)的配置,然后才去配置自定义插件(此实例为firstplugin这个module),即“> Configure project :firstplugin”信息的输出;
  • (4)最后就是依次执行自定义插件(此实例为firstplugin这个module)和被依赖module(此实例为app这个module)的任务队列,并且发现该自定义的HandleTemplateTask被加入到了被依赖module(此实例为app这个module)的任务队列中,即“> Task :app:handleTemplateTask”信息输出

        从这些输出信息中可以得到以下结论:

  • (1)在构建项目的生命周期中,被依赖module的配置阶段完成后,然后才会去配置自定义插件的配置阶段(这里重点标记下)
  • (2)在被依赖module的配置阶段中,当配置apply或plugins的时候,会回调到自定义插件的入口类的apply(project)方法
  • (3)当被依赖module以及该module下所有的plugins的配置阶段都完成后,会以此执行里面的task队列

二 在自定义插件中添加Task

        本次主要就是去尝试下是不是可以将自定义的Task直接添加到自定义插件的task队列中,因为这个Task所执行的功能本来就是该插件自身的作用。另外如果上述的功能可以实现,那现在怎么去做属性扩展呢?

        反思一下,那如果想把自定义的Task添加到自定义插件的task队列中,而入口类FirstPluginProject的apply(project)方法返回的project又是被依赖module,并且想到自定义插件的build.gradle就是该插件的Project对象,那么是不是就可以直接将自定义Task直接添加到该插件的build.gradle中呢?

        为了区分在Android 自定义Gradle插件的完整流程(三)3.Task中的属性扩展的添加task和扩展属性的方式:

  • (1)重新定义HandleTemplateTaskInProject继承DefaultTask为自定义Task
  • (2)重新定义TemplateSettingExtensionInProject为属性扩展类

        具体代码的内容可参见github上面的代码,在这里不在贴出来。

1.添加属性扩展  

  • (1)第一次想到的实现方案          

        上一篇中也介绍了可以通过project.getExtensions().create()的方式来添加属性扩展,所以在该插件的build.gradle调用如下代码:

project.getExtensions().create(TemplateSettingExtensionInProject.TAG, TemplateSettingExtensionInProject)

        但是发现当在被依赖module的build.gradle中使用templateSettingExtensionInProject的时候,直接语法错误,压根都不让编译。

        因为此时的project仅仅是该插件的Project对象,那么上述代码的调用仅仅是对该插件的Project对象添加了属性扩展,所以在被依赖module的build.gradle中根本无法使用templateSettingExtensionInProject

  • (2)第二次想到的实现方案

        既然现在project返回的是该插件的Project对象,那么是不是可以找到被依赖那个module的Project对象,然后在被依赖module的Project对象添加属性扩展呢?然后就有了下面的实现方案:

/**
 * 通过循环找到使用了该插件的module
 */
void createTemplateSettingExtensionInProjectForAllProject() 
    Set<Project> allProjectsInRoot = getRootProject().getAllprojects()
    for (Project project : allProjectsInRoot) 
        List<Plugin> plugins = project.getPlugins().toList()
        for (Plugin plugin : plugins) 
            //找到含有该插件的被依赖的project,添加该属性扩展
            if (project.getPlugins().findPlugin(com.wj.plugin.FirstPluginProject)) 
                project.getExtensions().create(TemplateSettingExtensionInProject.TAG, TemplateSettingExtensionInProject)
                return
            
        
    

        主要就是通过循环root project下所有的Project对象,然后看看哪个Project对象使用了该插件,然后在使用该插件的module下添加属性扩展。 

        本来信心十足,感觉现在已经可以在被依赖module中添加了属性扩展,并且在被依赖module中可以成功的添加templateSettingExtensionInProject属性扩展了,但是问题又来了,在编译的时候提示“ Could not find method templateSettingExtensionInProject() for arguments”。

        为什么会出现这种情况呢?

        这里就是前面在一 关系梳理中提到的需要重点标记的那个点“(1)在构建项目的生命周期中,被依赖module的配置阶段完成后,然后才会去配置自定义插件的配置阶段”。在被依赖module已经配置完成,而现在在插件的build.gradle中为才给被依赖module进行添加属性扩展,此时根本无法成功添加到被依赖module中了。

        所以暂时认为要能够在被依赖module中进行属性扩展,那么只能在入口类的apply(project)中为返回的project(此时返回的就是被依赖module的Project对象)添加属性扩展,代码如下:


class FirstPluginProject implements Plugin<Project> 

    @Override
    void apply(Project project) 
........
        /**为添加到build.gradle来增加扩展属性*/
        createExtensionsForInProject(project)
    

    void createExtensionsForInProject(project) 
        project.getExtensions().create(TemplateSettingExtensionInProject.TAG, TemplateSettingExtensionInProject)
    
........

        但是问题又来了,因为这个属性扩展是添加到了被依赖module的getExtensions()中,那么在插件的build.gradle中怎么获取到getExtensions()呢?我找到了曲线救国的方式:

/**
 * 找到设置了属性扩展的module中的属性扩展
 * @return
 */
TemplateSettingExtensionInProject getTemplateSettingExtensionInProject() 
    Set<Project> allProjectsInRoot = getRootProject().getAllprojects()
    for (Project project : allProjectsInRoot) 
        TemplateSettingExtensionInProject extension = project.getExtensions().findByName(TemplateSettingExtensionInProject.TAG)
        if (extension != null) 
            return extension
        
    
    return null

        还是循环root project下的所有Project对象,然后获取哪个 Project对象的getExtensions()的内容不为null即可。

        现在终于可以在被依赖module下可以对自定义插件进行属性扩展,现在在app的这个module的build.gradle中进行配置templateSettingExtensionInProject,如下:

templateSettingExtensionInProject 
    compileSdk = "1.0.0"
    interfaceSourceDir = file("src/main/java/mvp")

2.将HandleTemplateTaskInProject添加到插件的project中

        将HandleTemplateTaskInProject添加到自定义插件的build.gradle,代码如下:

/**(2)将功能的Task添加到app这个project的任务队列中*/
task handleTemplateTaskInProject(type: HandleTemplateTaskInProject) 

this.afterEvaluate 

    //必须设置input,否则会抛出"No value has been specified for property"
    String path = "/Users/j1/Documents/android/code/AndroidPlugin/firstplugin/src/main/groovy/com/wj/plugin/extension"
    TemplateSettingExtensionInProject extension = getTemplateSettingExtensionInProject()
    if (extension != null) 
        handleTemplateTaskInProject.setFileSourceDir(extension.interfaceSourceDir)
    
    handleTemplateTaskInProject.setFileFormat(".java")
    tasks.compileJava.dependsOn(handleTemplateTaskInProject)

       因为TemplateSettingExtensionInProject只有在项目配置完成之后,才可以通过project.getExtensions().findByName()取到配置的值,所以在this.afterEvaluate将HandleTemplateTaskInProject添加到插件的任务队列中。

        另外注意有一点:当增加HandleTemplateTaskInProject出现编译不通过的时候(譬如HandleTemplateTaskInProject里面新增或删除属性),需要删除plugins/里面的内容之后,删除在root project和被依赖的module中添加的插件依赖,重新编译该项目然后uploadArchives,最后在将该依赖添加到root project中 。

         按照  Android Gradle插件开发初次交手(一)4.插件发布方式进行发布插件,然后使用Android Studio编译整个工程,发现Build窗口输出的内容如下:

> Configure project :app
<<<<<<<settings.gradle<<<<<<<<<<    beforeProject Project name = app
%%%%%%%%% FirstPluginProject %%%%%%%%% Apply the First Plugin Project
~~~~~~GlobalGradleProject~~~~~~~~   Apply the Global Gradle Project 
@@@@@@@@@@@@@@ app @@@@@@@@@@@@@   afterEvaluate

> Configure project :firstplugin
<<<<<<<settings.gradle<<<<<<<<<<    beforeProject Project name = firstplugin
<<<<<<<settings.gradle<<<<<<<<<<     projectsEvaluated 已经完成Gradle构建的配置阶段
//此时handleTemplateTaskInProject已经添加到firstplugin中

> Task :firstplugin:handleTemplateTaskInProject
%%%%%%%%% FirstPluginProject %%%%%%%%%  HandleTemplateTaskInProject is running 
%%%%%%%%% FirstPluginProject %%%%%%%%%  Set the file format is " .java
%%%%%%%%% FirstPluginProject %%%%%%%%%  Set the file source dir is " /Users/j1/Documents/android/code/AndroidPlugin/app/src/main/java/mvp

> Task :firstplugin:compileJava NO-SOURCE
> Task :firstplugin:compileGroovy UP-TO-DATE
> Task :firstplugin:processResources UP-TO-DATE
> Task :firstplugin:classes UP-TO-DATE
> Task :firstplugin:jar UP-TO-DATE
> Task :firstplugin:makeJar UP-TO-DATE
> Task :firstplugin:assemble UP-TO-DATE
> Task :firstplugin:compileTestJava NO-SOURCE
> Task :firstplugin:compileTestGroovy NO-SOURCE
> Task :firstplugin:processTestResources NO-SOURCE
> Task :firstplugin:testClasses UP-TO-DATE

> Task :app:firstTask

        此时发现 handleTemplateTaskInProject已经添加到firstplugin这个插件中。

        具体的代码已经上传到至https://github.com/wenjing-bonnie/AndroidPlugin.git的firstplugin目录的相关内容。因为逐渐在这基础上进行迭代,可回退到Gradle_4.0该tag下可以查看相关内容。

三 总结

  •  1.在构建项目的生命周期内,被依赖的module的配置阶段完成后,才去进行apply或plugins下的插件的配置阶段;所以如果想在自定义插件中对被依赖module的project进行配置内容,必须在入口类的apply(project)中;
  • 2.当所有的module的配置阶段完成后,然后依次执行所有的任务队列;
  • 3.对project对象的属性扩展必须放到对应的project中,否则会出现语法错误;
  • 4.可在插件的build.gradle中添加自定义的task,那么该task就在该插件的任务队列中执行;
  • 5.要想能够让被依赖的module进行属性扩展,那么必须在入口类的apply(project)中,对返回的project对象进行属性扩展

        有了多次的碰壁,终于现在对概念也清晰了,在解决一些问题的时候,游刃有余,并且总能找到解决方案。

        加油!!!!

以上是关于Android 自定义Task添加到任务队列的主要内容,如果未能解决你的问题,请参考以下文章

Android 自定义Gradle插件的完整流程

Android Gradle 插件自定义 Gradle 任务 ③ ( Gradle 自定义任务创建方法 Project#task 函数 | Task#doFirst 函数用法 )

Android Gradle 插件自定义 Gradle 任务 ③ ( Gradle 自定义任务创建方法 Project#task 函数 | Task#doFirst 函数用法 )

Android 自定义Gradle插件的多层属性扩展

Android Gradle 插件自定义 Gradle 任务 ④ ( Task#doLast 函数用法 | 自定义 Gradle 任务代码示例 )

Android Gradle 插件自定义 Gradle 任务 ④ ( Task#doLast 函数用法 | 自定义 Gradle 任务代码示例 )