Gradle插件开发入门

    平常开发过程中一般只要对gradle里的一些配置懂就可以,但如果想要一些高级一点的处理功能比如自定义打包Jar,或者最近本人在研究android上面的AOP编程,其中用到了AspectJ参与编绎过程,这种就要自己定制一下gradle脚本,但只是修改gradle文件如果脚本内容一多项目一多维护起来就非常麻烦,使得原来的脚本并不清晰优雅,这样就得了解gradle插件开发原理把脚本写在一个插件。本文就以一个AOP实例讲解如何开发一个Gradle插件。

需求背景

我的需求是使用注解实现对一个方法计算运行时的耗时,希望效果如下:

public class MainActivity extends AppCompatActivity {

    @Override
    @Trace(pkg = "test")
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

05-25 11:03:34.939 7192-7192/org.cmdmac.aoptest W/TraceAspect: test.onCreate cost 54.054038ms

在onCreate中定义@Trace注解,运行有上面的Log。
AOP原理这里不具体阐述,来说说工程依赖关系。
首先有个只p定义了Trace注解的Library工程名为annonation,然后有个AOP实现的Library工程aop,还有个使用注解的工程app.

根据AOP实现原理,需要对aop和app两个工程使用aspectj对生成的class进行编译,如果不使用gradle插件,则需要在aop下面的build.gralde使用如下脚本:

final def variants = project.android.libraryVariants
JavaCompile javaCompile = variant.javaCompile
//编译完后使用aspectj来编译
javaCompile.doLast {
    String[] args = [
        "-showWeaveInfo",
        "-1.5",
        "-inpath", javaCompile.destinationDir.toString(),
        "-aspectpath", javaCompile.classpath.asPath,
        "-d", javaCompile.destinationDir.toString(),
        "-classpath", javaCompile.classpath.asPath,
        "-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)
    ]
    //log.debug "ajc args: " + Arrays.toString(args)

    MessageHandler handler = new MessageHandler(true);
    new Main().run(args, handler);
    //处理编译消息
    for (IMessage message : handler.getMessages(null, true)) {
      switch (message.getKind()) {
        case IMessage.ABORT:
        case IMessage.ERROR:
        case IMessage.FAIL:
          //log.error message.message, message.thrown
          break;
         //other case
      }
    }
}

还要在app的build.gradle使用如下脚本:

//library工程使用libraryVariants,app工程使用applicationVariants
final def variants = project.android.applicationVariants
JavaCompile javaCompile = variant.javaCompile
//跟上面一样,此处省略一万字

可以看到上面两个build.gradle文件中使用了重复的逻辑,占用了很大的篇幅,实际只有获取variants时不一样,因此如果能用gradle插件实现这段逻辑,使用时应用这个插件,build.gradle文件会清爽很多,以后也只要维护插件代码即可。简而言之最终想达到的效果就是只在build.gradle文件引用插件代替上面一大段:

apply plugin: 'org.cmdmac.aop'

那如何实现这么一个插件呢?

Gradle插件开发过程

1. 首先开发gradle插件需要使用AndroidStudio新建一个library工程名为aopplugin,把src/main下的java目录重命名为groovy(使用groovy语言来开发插件)、res目录重命名为resources,接下来删掉resources目录下的所有目录和文件并把目录结构调整为如下:

META-INF //目录
    gradle-plugins //目录
        org.cmdmac.aop.properties //插件属性文件

org.cmdmac.aop.properties是插件的配置文件,注意!org.cmdmac.aop就是apply plugin时的插件名

2. 做好这些工作后开始写你的插件代码吧,在groovy目录下建立自己的package和groovy插件文件,比如,我这里是用的包名是org.cmdmac.aopplugin下面的groovy文件是HugoPlugin.groovy,在这个文件中实现插件逻辑:

//gradle插件要实现Plugin<Project>接口,void apply(Project)数就是调用apply plugin 'xxx'时会调用的方法
class HugoPlugin implements Plugin<Project> {
  @Override void apply(Project project) {
    def hasApp = project.plugins.withType(AppPlugin)
    def hasLib = project.plugins.withType(LibraryPlugin)
    if (!hasApp && !hasLib) {
      throw new IllegalStateException("'android' or 'android-library' plugin required.")
    }

    final def log = project.logger
    final def variants
    //根据工程类型判断使用不同的variant,这样在不同的工程apply时就会使用不同的variant啦
    if (hasApp) {
      variants = project.android.applicationVariants
    } else {
      variants = project.android.libraryVariants
    }
    JavaCompile javaCompile = variant.javaCompile
    //跟前面不用插件形式的代码一样,复制过来就可以了,此处省略
  }
}

3. 实现了插件逻辑后再在org.cmdmac.aop.properties文件中指定插件的实现类,其实就是入口类

implementation-class=org.cmdmac.aopplugin.HugoPlugin

4. 好了,你现在已经写好插件了,但还没编译和生成,还不能被其他工程使用,接下来需要配置插件工程aopplugin的build.gradle文件

apply plugin: 'groovy'
apply plugin: 'maven'
dependencies {
    //指定编译插件需要的依赖
    implementation gradleApi()
    implementation localGroovy()
    implementation 'com.android.tools.build:gradle:3.1.2'
    //aspectj需要到的类
    implementation 'org.aspectj:aspectjtools:1.8.5'
}
//配置group和version;gradle插件的命名方式:group:artifactId:version我们这里会是org.cmdmac.aop.aoplugin.1.0.0,插件名artifactId默认是使用插件工程名的
group='org.cmdmac.aop'
version='1.0.0'
//生成插件的task
uploadArchives {
    repositories {
        mavenDeployer {
            //我们发布到本地的地址,可以发布到jcenter上,具体可google下相关教程
            repository(url: uri('../repo'))
        }
    }
}
5. 写好之后就可以执行uploadArchives任务就可以在与aopplugin目录平级有个repo目录下找到插件生成的文件。

到这里就可以在工程里引用插件来开发了,首先在Project级的build.gradle引入插件地址:

buildscript {
    repositories {
        jcenter()
        google()
        mavenLocal()
        maven {
            //使用本地仓库,即插件目录地址
            url "/home/fengzhiping/work/android/AOP4Android/repo"
        }

    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.1.2'
        //引用插件
        classpath 'org.cmdmac.aop:aopplugin:1.0.0'
    }
}

然后在aop和app目录下的gradle应用插件

apply plugin: 'org.cmdmac.aop'
最后配置好app、aop、annotation依赖关系,在app工程使用一开始使用的文件构建apk运行就能看到onCreate执行时间了。

上面例子在github https://github.com/Cmdmac/AOPAndroid

总结

gradle相比传统的项目配置方案确实高效了许多,其中一个重要的原因就是因为gradlea使用可以用groovy语言来描述,要想更深入掌握gradle也用groovy语言有更深的了解,除此之外,要想在实际项目中灵活使用gradle还必须熟悉常用的插件,比如这里在android项目中就得了解如何区分是library还是aplication工程,每个编译task如何得到,插件里什么变量以及如何自定义配置变量等。这些内容准备下次再分享。

猜你喜欢

转载自blog.csdn.net/cmdmac/article/details/80507319