• 怎样使用Android Studio开发Gradle插件


    缘由

    首先说明一下为什么会有这篇文章。前段时间,插件化以及热修复的技术非常热,Nuwa热修复的工具NuwaGradle,携程动态载入技术DynamicAPK,还有希望做最轻巧的插件化框架的Small。这三个App有一个共同的地方就是大量的使用了Gradle这个强大的构建工具,除了携程的框架外,另外两个都公布了独立的Gradle插件提供自己主动化构建插件,或者生成热修复的补丁。所以学习一下Gradle插件的编写还是一件十分有意义的事。

    插件类型

    Gradle的插件一般有这么几种:

    • 一种是直接在项目中的gradle文件里编写。这种方式的缺点是无法复用插件代码。在其它项目中还得复制一遍代码(或者说说复制一遍文件)
    • 还有一种是在独立的项目里编写插件,然后公布到中央仓库。之后直接引用就能够了。长处就是可复用。就和上面的Nuwa和Small一样。

    Gradle相关语法

    本篇文章不会详细说明Gradle相关的语法。假设要学习gradle相关的东西,请查看Gradle for Android

    Gradle插件开发

    Gradle插件是使用Groovy进行开发的,而Groovy事实上是能够兼容Java的。

    Android Studio事实上除了开发Android App外,全然能够胜任开发Gradle插件这一工作,以下来讲讲详细怎样开发。

    1. 首先,新建一个Android项目。
    2. 之后。新建一个Android Module项目,类型选择Android Library。
    3. 将新建的Module中除了build.gradle文件外的其余文件全都删除,然后删除build.gradle文件里的全部内容。

    4. 在新建的module中新建文件夹src,接着在src文件文件夹下新建main文件夹,在main文件夹下新建groovy文件夹。这时候groovy文件夹会被Android识别为groovy源代码文件夹。

      除了在main文件夹下新建groovy文件夹外,你还要在main文件夹下新建resources文件夹。同理resources文件夹会被自己主动识别为资源文件夹。

      在groovy文件夹下新建项目包名。就像Java包名那样。resources文件夹下新建文件夹META-INF。META-INF文件夹下新建gradle-plugins文件夹。这样。就完毕了gradle 插件的项目的总体搭建,之后就是小细节了。眼下,项目的结构是这种。

    这里写图片描写叙述

    打开Module下的build.gradle文件,输入

    apply plugin: 'groovy'
    apply plugin: 'maven'
    
    dependencies {
        compile gradleApi()
        compile localGroovy()
    }
    
    repositories {
        mavenCentral()
    }

    以下我们在包名下新建一个文件,命名为PluginImpl.groovy,注意有groovy后缀。然后在里面输入,注意包名替换为你自己的包名。

    package cn.edu.zafu.gradle
    
    import org.gradle.api.Plugin
    import org.gradle.api.Project
    
    public class PluginImpl implements Plugin<Project> {
        void apply(Project project) {
           project.task('testTask') << {
                println "Hello gradle plugin"
            }
        }
    }

    然后在resources/META-INF/gradle-plugins文件夹下新建一个properties文件。注意该文件的命名就是你仅仅有使用插件的名字。这里命名为plugin.test.properties,在里面输入

    implementation-class=cn.edu.zafu.gradle.PluginImpl

    注意包名须要替换为你自己的包名。

    这样就完毕了最简单的一个gradle插件。里面有一个叫testTask的Task。运行该task后会输出一段文字。就像当初我们输出HelloWorld一样。

    公布到本地仓库

    接着。我们须要将插件公布到maven中央仓库。我们将插件公布到本地仓库就好了。在module项目下的buidl.gradle文件里增加公布的代码。

    repositories {
        mavenCentral()
    }
    group='cn.edu.zafu.gradle.plugin'
    version='1.0.0'
    
    uploadArchives {
        repositories {
            mavenDeployer {
                repository(url: uri('../repo'))
            }
        }
    }

    上面的group和version的定义会被使用,作为maven库的坐标的一部分,group会被作为坐标的groupId,version会被作为坐标的version,而坐标的artifactId组成即module名,我们让其取一个别名moduleName。然后maven本地仓库的文件夹就是当前项目文件夹下的repo文件夹。
    这时候。右側的gradle Toolbar就会在module下多出一个task

    这里写图片描写叙述

    点击uploadArchives这个Task。就会在项目下多出一个repo文件夹,里面存着这个gradle插件。

    这里写图片描写叙述

    文件夹就像上图这样,详细文件夹结构和你的包名等一系列有关,time是我的module名。

    公布到本地maven仓库后,我们就使用它,在叫app的android项目下的gradle.build的文件里增加

    buildscript {
        repositories {
            maven {
                url uri('../repo')
            }
        }
        dependencies {
            classpath 'cn.edu.zafu.gradle.plugin:time:1.0.0'
        }
    }
    apply plugin: 'plugin.test'

    apply plugin后面引號内的名字就是前文plugin.test.properties文件的文件名称。而class path后面引號里的内容,就是上面grade中定义的group。version以及moduleName所共同决定的,和maven是一样的。

    同步一下gradle,右側app下other分类下就会多出一个testTask,双击运行这个Task,控制台就会输出刚才我们输入的字符串

    这里写图片描写叙述

    公布到Jcenter仓库

    接下来我们将其公布到jcenter中央仓库。參考之前写的一篇文章 使用Android Studio将开源库公布到Jcenter中央库

    在项目根文件夹下的build.gradle文件里增加。

    dependencies {
            classpath 'com.android.tools.build:gradle:2.0.0-beta6'
            classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0'
            classpath 'com.github.dcendents:android-maven-plugin:1.2'
        }

    在项目根路径下新建bintray.gradle文件,输入

    apply plugin: 'com.jfrog.bintray'
    apply plugin: 'maven-publish'
    
    def projectName = "timePlugin"
    def mavenDesc = 'your desc'
    def baseUrl = 'https://github.com/yourBaseUrl'
    def siteUrl = baseUrl
    def gitUrl = "${baseUrl}/yourGitUrl"
    def issueUrl = "${baseUrl}/yourGitIssueUrl"
    
    def licenseIds = ['Apache-2.0']
    def licenseNames = ['The Apache Software License, Version 2.0']
    def licenseUrls = ['http://www.apache.org/licenses/LICENSE-2.0.txt']
    def inception = '2016'
    
    def username = 'lizhangqu'
    
    install {
        repositories {
            mavenInstaller {
                pom.project {
                    // Description
                    name projectName
                    description mavenDesc
                    url siteUrl
    
                    // Archive
                    groupId project.group
                    artifactId archivesBaseName
                    version project.version
    
                    // License
                    inceptionYear inception
                    licenses {
                        licenseNames.eachWithIndex { ln, li ->
                            license {
                                name ln
                                url licenseUrls[li]
                            }
                        }
                    }
                    developers {
                        developer {
                            name username
                        }
                    }
                    scm {
                        connection gitUrl
                        developerConnection gitUrl
                        url siteUrl
                    }
                }
            }
        }
    }
    
    task sourcesJar(type: Jar) {
        from sourceSets.main.allGroovy
        classifier = 'sources'
    }
    
    
    task javadocJar(type: Jar, dependsOn: groovydoc) {
        from groovydoc.destinationDir
        classifier = 'javadoc'
    }
    
    artifacts {
        archives javadocJar
        archives sourcesJar
    }
    
    
    bintray {
        user = BINTRAY_USER
        key = BINTRAY_KEY
        configurations = ['archives']
        pkg {
            repo = 'maven'
            name = projectName
            desc = mavenDesc
            websiteUrl = siteUrl
            issueTrackerUrl = issueUrl
            vcsUrl = gitUrl
            labels = ['gradle', 'plugin', 'time']
            licenses = licenseIds
            publish = true
            publicDownloadNumbers = true
        }
    }

    将相应的描写叙述性文字改动为你自己的信息。尤其是最前面的一系列的def定义,然后在gradle.properties文件里增加BINTRAY_USER和BINTRAY_KEY。

    在你的module中apply该grade文件

    apply from: '../bintray.gradle'

    右側的gradle的toolbar就会多出几个task

    这里写图片描写叙述

    之后我们先运行other下的install这个task,再运行bintrayUpload这个task。假设不出意外,就上传了。之后不要忘记到后台add to jcenter

    成功add到jcenter之后就会有link to jcenter的字样

    这里写图片描写叙述

    耐心等待add to center成功的消息,之后就能够直接引用了。将module下的gradle文件maven部分的定义

            maven {
                url uri('../repo')
            }

    前面增加

        jcenter()

    终于的内容例如以下

    buildscript {
        repositories {
            jcenter()
            maven {
                url uri('../repo')
            }
        }
        dependencies {
            classpath 'cn.edu.zafu.gradle.plugin:time:1.0.0'
        }
    }
    
    apply plugin: 'plugin.test'

    就是这么简单,再次运行一下測试下是否成功。

    最佳实践

    最佳实践的来源是源自multidex,为什么呢。由于近期当方法数超了之后,假设选择multidex,编译的过程就会慢非常多非常多,为了检測究竟是哪一步的耗时。须要编写一个插件来统计各个task运行的时间,因此就有了这么一个最佳实践。

    在PluginImpl同级文件夹下新建TimeListener.groovy文件。输入

    package cn.edu.zafu.gradle
    
    import org.gradle.BuildListener
    import org.gradle.BuildResult
    import org.gradle.api.Task
    import org.gradle.api.execution.TaskExecutionListener
    import org.gradle.api.initialization.Settings
    import org.gradle.api.invocation.Gradle
    import org.gradle.api.tasks.TaskState
    import org.gradle.util.Clock
    
    class TimeListener implements TaskExecutionListener, BuildListener {
        private Clock clock
        private times = []
    
        @Override
        void beforeExecute(Task task) {
            clock = new org.gradle.util.Clock()
        }
    
        @Override
        void afterExecute(Task task, TaskState taskState) {
            def ms = clock.timeInMs
            times.add([ms, task.path])
            task.project.logger.warn "${task.path} spend ${ms}ms"
        }
    
        @Override
        void buildFinished(BuildResult result) {
            println "Task spend time:"
            for (time in times) {
                if (time[0] >= 50) {
                    printf "%7sms  %s
    ", time
                }
            }
        }
    
        @Override
        void buildStarted(Gradle gradle) {}
    
        @Override
        void projectsEvaluated(Gradle gradle) {}
    
        @Override
        void projectsLoaded(Gradle gradle) {}
    
        @Override
        void settingsEvaluated(Settings settings) {}
    }
    

    然后将PluginImpl文件里的apply方法改动为

    void apply(Project project) {
           project.gradle.addListener(new TimeListener())
        }

    完毕后打包公布到jcenter()。之后你仅仅要引用了该插件,就会统计各个task运行的时间,比方运行app,就会输出像以下的信息。

    这里写图片描写叙述

    最佳实践的末尾,推广一下这个插件,这个插件我已经将其公布到jcenter仓库。假设要使用的话增加以下的代码就可以

    buildscript {
        repositories {
            jcenter()
        }
        dependencies {
            classpath 'cn.edu.zafu.gradle.plugin:time:1.0.0'
        }
    }
    
    apply plugin: 'plugin.time'

    传递參数

    上面的是小试牛刀了下,接下来我们须要获得自己定义的參数。

    首先依照上面的步骤新建一个module。新建PluginExtension.groovy,输入

    public class PluginExtension {
        def param1 = "param1 defaut"
        def param2 = "param2 defaut"
        def param3 = "param3 defaut"
    }

    然后我们希望能传入嵌套的參数,再新建一个PluginNestExtension.groovy。输入

    public class PluginNestExtension {
        def nestParam1 = "nestParam1 defaut"
        def nestParam2 = "nestParam2 defaut"
        def nestParam3 = "nestParam3 defaut"
    }

    然后新建一个CustomTask.groovy。继承DefaultTask类,使用 @TaskAction注解标注实现的方法

    public class CustomTask extends DefaultTask {
    
        @TaskAction
        void output() {
            println "param1 is ${project.pluginExt.param1}"
            println "param2 is ${project.pluginExt.param2}"
            println "param3 is ${project.pluginExt.param3}"
            println "nestparam1 is ${project.pluginExt.nestExt.nestParam1}"
            println "nestparam2 is ${project.pluginExt.nestExt.nestParam2}"
            println "nestparam3 is ${project.pluginExt.nestExt.nestParam3}"
        }
    }

    仅仅是做了拿到了參数,然后做最简单的输出操作,使用 ${project.pluginExt.param1}${project.pluginExt.nestExt.nestParam1}等拿到外部的參数。

    别忘了在META-INF/gradle-plugins文件夹下新建properties文件指定插件的接口实现类。

    复制之前新建的PluginImpl.groovy到包下,改动apply方法

    public class PluginImpl implements Plugin<Project> {
        void apply(Project project) {
            project.extensions.create('pluginExt', PluginExtension)
            project.pluginExt.extensions.create('nestExt', PluginNestExtension)
            project.task('customTask', type: CustomTask)
        }
    }

    将插件公布到本地maven后,进行引用。

    buildscript {
        repositories {
            maven {
                url uri('../repo')
            }
        }
        dependencies {
            classpath 'cn.edu.zafu.gradle.plugin:test:1.0.0'
        }
    }
    apply plugin: 'plugin.test'

    定义外部參数。这里我们定义了param1,param2,nestParam1,nestParam2。此外param3和nestParam3保持默认。

    pluginExt {
        param1 = 'app param1'
        param2 = 'app param2'
        nestExt{
            nestParam1='app nestParam1'
            nestParam2='app nestParam2'
    
        }
    }

    同步一下gradle。运行customTask。

    这里写图片描写叙述

    上面的代码非常easy,不用解释也能看到,所以不再解释了。

    參考文章

    源代码

    最后上本篇文章的源代码
    http://download.csdn.net/detail/sbsujjbcy/9451566

  • 相关阅读:
    Thinking in Java,Fourth Edition(Java 编程思想,第四版)学习笔记(二)之Introduction to Objects
    Thinking in Java,Fourth Edition(Java 编程思想,第四版)学习笔记(一)之Introduction
    JMock2入门
    webWMS开发过程记录(六)- 详细设计之系统管理
    webWMS开发过程记录(五)- 详细设计之系统界面框架设计
    基于gensim深入自然语言处理
    sublime text 搭建anconda的python开发环境遇到问题
    文本分类
    简介:机器学习模型在数学就是解决代价函数的最优化问题
    学习笔记一 线性代数
  • 原文地址:https://www.cnblogs.com/zsychanpin/p/7384031.html
Copyright © 2020-2023  润新知