Gradle插件练习地址:https://github.com/peiniwan/ASMLifeCycleTest

什么是Gradle

Gradle 是一个基于 Apache Ant 和 Apache Maven 概念的项目自动化构建工具。Gradle 就是工程的管理,帮我们做了依赖、打包、部署、发布、各种渠道的差异管理等工作。

Gradle脚本是基于Groovy语言来编译执行的,Java、Groovy、Kotlin等都是基于JVM运行的,所以他们在语法上共性很多,熟悉Java的同学应该对Groovy上手很快

编写方法

在 Android 下的 gradle 插件共分为 两大类:

  • 脚本插件:同普通的 gradle 脚本编写形式一样,可以直接写在build.gradle文件中,也可以自己新建一个 gradle 脚本文件中写
  • 对象插件:通过插件全路径类名或 id 引用,它主要有 三种编写形式,如下所示:

    1)在当前构建脚本下直接编写。

    2)在 buildSrc 目录下编写。

    3)在完全独立的项目中编写。

buildSrc

由于buildSrc目录是gradle默认的目录之一,该目录下的代码会在构建是自动编译打包,并被添加到buildScript中的classpath下,所以不需要任何额外的配置,就可以直接被其他模块的构建脚本所引用。

这就是:buildScript

在buildSrc/src/main目录下,再分别创建groovy、resources文件夹。

随便定义的需要自己写classpath:

优点:

  • 项目构建时,Gradle 会自动编译项目目录下的 buildSrc 文件夹下的构建脚本和源码,并将其添加到项目构建脚本的 classpath 中,因此在使用 buildSrc 中创建的插件时,无需再手动指定 classpath(依赖的名字)(当然也可以自己创建id)
  • buildSrc 文件夹中构建脚本和 Gradle 插件同一项目均可见,因此同一项目中的其他模块也可以使用 buildSrc 中创建的插件
  • 不需要 uploadArchives task

缺点:

此处创建的插件对外部项目不可见,无法在其他项目中复用

id引入

引用的方式可以是通过类名引用,也可以通过给插件映射一个id,然后通过id引用。

通过类名引用插件的需要使用全限定名,也就是需要带上包名,或者可以先导入这个插件类,如下

// 在app模块下的build.gradle文件中引用

apply plugin:com.wings.gradle.CustomBuildSrcPlugin

或者

// 在app模块下的build.gradle文件中引用

import com.wings.gradle.CustomBuildSrcPlugin

apply plugin: CustomBuildSrcPlugin

通过简单的id的方式,我们可以隐藏类名等细节,使的引用更加容易。映射的方式很简单,在buildSrc目录下创建resources/META-INF/gradle-plugins/xxx.properties,这里的xxx也就是所映射的id,这里我们假设取名CustomPlugin。具体结构可参考上文buildSrc目录结构。

基础概念

Extension

为了能让 App 传入相关的版本信息和生成的版本信息文件路径,我们需要一个用于配置版本信息的 Extension,其实质就是一个实体类

与创建扩展属性一样,扩展Task也需要在project中创建注入。

project.extensions.create("releaseInfo", ReleaseInfoExtension)

自定义Task

右边就都是task

  • 使用自定义扩展属性 Extension 仅仅是为了让使用插件者有配置插件的能力。而插件还得借助自定义 Task 来实现相应的功能
  • 创建扩展属性一样,扩展Task也需要在project中创建注入
// 创建Task
project.tasks.create("updateReleaseInfo", ReleaseInfoTask)
  • task 的作用就是通过实现自定义的 Extension,可以在 Gradle 脚本中增加类似 android 这样命名空间的配置,Gradle 可以识别这种配置,并读取里面的配置内容。。
  • 一个Task表示一个逻辑上较为独立的执行过程,比如编译Java源代码,拷贝文件,打包Jar文件,甚至可以是执行一个系统命令或者调用Ant。另外,一个Task可以读取和设置Project的Property以完成特定的操作。
  • 一个Task是由一序列Action组成的,当运行一个Task的时候,这个Task里的Action序列会按照顺序执行

构建生命周期

每次构建的本质其实就是执行一系列的Task,某些Task可能依赖其他Task,那些没有依赖的Task总会被最先执行,而且每个Task只会被执行一遍,每次构建的依赖关系是在构建的配置阶段确定的,在gradle构建中,构建的生命周期主要包括以下三个阶段:

初始化(Initialization)

构建工具会根据每个build.gradle文件创建出一个Project实例,初始化阶段会执行项目根目录下的Settings.gradle文件,来分析哪些项目参与构建。

include ':app'

配置(Configuration)

执行(Execution)

Plugin

作用

  • 模块化构建脚本的功能
  • 公共的功能可以抽取出来成为插件,可以供多个 build.gradle 使用,增加复用性。

和task的关系

如果有个你想要在好几个项目中重用的Gradle task集合,把这些task提取到一个自定义的plugin中是有意义的。这使得重用你自己的build逻辑和与他人共享该逻辑都是可能的。

Transformer

在 Booster 中,跟字节码相关的操作都是通过 Transformer 来完成,它是对字节码转换的简单抽象,以字节码的二进制做为输入,经过转换后,输出字节码二进制,它与具体使用哪种字节码操作框架无关,开发者可以自己选择跟字节码操作框架相关的特定实现, Booster 提供了两种实现:

基于 ASM 的实现:AsmTransformer

基于 Javassist 的实现:JavassistTransformer

Transform 可以被看作是Gradle 在编译项目时的一个 task,在 .class 文件转换成 .dex 的流程中会执行这些 task,对所有的 .class 文件(可包括第三方库的 .class)进行转换,转换的逻辑定义在 Transform 的 transform 方法中。实际上平时我们在 build.gradle 中常用的功能都是通过 Transform 实现的,比如混淆(proguard)、分包(multi-dex)、jar 包合并(jarMerge)

class AddCodePlugin implements Plugin<Project> {
void apply(Project project) {
project.android.registerTransform(new AddCodeTransform(project))
}
}

写法



其实就是:把输入内容写入到作为输出内容

输出地址不是由你任意指定的。而是根据输入的内容、作用范围等由TransformOutputProvider生成,比如,你要获取输出路径:

 String dest = outputProvider.getContentLocation(directoryInput.name,
directoryInput.contentTypes, directoryInput.scopes, Format.DIRECTORY) FileUtils.copyDirectory(directoryInput.file, dest)

Transform的inputs有两种类型,一种是目录,一种是jar包,要分开遍历

一旦注册了transform,就要处理输入和输出(默认实现是没有处理的),否则编译失败。

字节码操作框架

ASM vs Javassist

https://booster.johnsonlee.io/developer/bytecode-engineering-framework.html#asm-vs-javassist

Transform API 起因

从 Android Gradle Plugin 1.5.0-beta1 开始,为了简化注入自定义 class 的操作,Android 提供了 Transform API,允许第三方插件在 class 文件被转换成 dex 之前对其进行修改,在此之前,如果要实现同样的操作,只能通过 Hook Task 的方式才能做到

参数说明

具体看代码

解释说明:Transform 主要作用是检索项目编译过程中的所有文件。通过这几个方法,我们可以对自定义 Transform 设置一些遍历规则,具体如下:

getName:

设置我们自定义的 Transform 对应的 Task 名称。Gradle 在编译的时候,会将这个名称显示在控制台上。比如:

Task :app:transformClassesWithXXXForDebug。

getInputType:

在项目中会有各种各样格式的文件,通过 getInputType 可以设置 LifeCycleTransform 接收的文件类型,此方法返回的类型是 Set<QualifiedContent.ContentType> 集合。

Gradle用处

gradle插件修改第三方代码

1、我们知道在打包过程中,可以通过动态修改字节码,来进行插桩,实现埋点等业务,那么,在什么时机插入呢?

2、随着项目越来越大,编译项目的时间会越来越长,我们需要统计各个任务的执行时间,来优化我们的打包编译速度,那么,如何统计呢?

3、在我们的项目、第三方库和系统遇到一些bug的时候,我们有没有什么比较好的hook方法,对我们的代码做到无侵入?

好文章

调试gradle

https://www.jianshu.com/p/6bbe9352f75d 也可以

gradle插件发布

通过自定义Gradle插件修改编译后的class文件

Gradle插件实战之编译期修改代码

  • 开源库和自己写的插入代码注意不要混淆
  • buildSrc中build.gradle的AGP版本要和app模块中一致
  • 插入代码引用的类要使用全路径
  • 插入代码中用到的类需要将类路径添加到classPool中,否则会编译不过

    buildSrc不要在settings.gradle中配置
  • 不管我们有没有修改jar的操作,也要拷贝到目标路径

Android ASM框架详解

Java学习之 javassist

自定义Gradle插件+ASM 实战

常见问题

  • Could not find implementation class 'xxx' 的话
implementation-class=com.lqr.gradle.study.GradleStudyPlugin
// 如果报错 Could not find implementation class 'xxx' 的话,一般是类全路径有问题,默认包不需要写包路径,修改如下即可:
// implementation-class=GradleStudyPlugin
  • 重新部署插件时,需要先在 app module 的 build.gradle 中将插件依赖注释,否则报错。
  • 不生效时,可以先注释,编译,再打开试试

Android Gradle插件的更多相关文章

  1. [Android]Gradle 插件 DiscardFilePlugin(class注入&清空类和方法)

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/6732128.html Android Gradle 插件 Di ...

  2. 《Gradle权威指南》--Android Gradle插件

    No1: Android Gradle插件分类 App插件id:com.android.application Library插件id:com.android.library Test插件id:com ...

  3. Android Gradle 学习笔记(七):Android Gradle 插件

    我们知道Android Gradle其实就是一个Gradle的一个第三方插件,它是由Google的Android团队开发的,基于Gradle构建的,和Android Studio完美搭配.相比于旧的构 ...

  4. 【Gradle】Android Gradle 插件

    Android Gradle 插件 Android Gradle 插件简介 从Gradle角度来看,Android其实是Gradle的一个第三方插件,它是由Google的Android团队开发的.但从 ...

  5. Gradle系列之Android Gradle插件

    原文发于微信公众号 jzman-blog,欢迎关注交流. 通过前面几篇文章学习了 Gradle 基础知识以及 Gradle 插件相关的知识,关于 Gradle 及其插件相关知识请先阅读下面几篇文章: ...

  6. Android Gradle插件(plugin)版本(version)与Gradle、SDK Build Tools版本关系

    具体关系如下图: 比如,Android Studio 2.0发布,其中有个新功能“Instant Run”,需要Android Gradle Plugin版本2.0.0以上,那么我们项目的.gradl ...

  7. Android端生成META-INF信息文件的Gradle插件 RapidMetaInfPlugin

    来源博客:Wang Jie's Blog 本文链接:<http://blog.wangjiegulu.com/2018/02/05/Android端生成META-INF信息文件的Gradle插件 ...

  8. Android官方技术文档翻译——Gradle 插件用户指南(7)

    本文译自Android官方技术文档<Gradle Plugin User Guide>,原文地址:http://tools.android.com/tech-docs/new-build- ...

  9. Android Gradle 学习笔记(六):Gradle 插件

    Gradle 本身提供了一些基本的概念和整体核心的框架,其他用于描述真实使用场景的都可以通过插件扩展的方式来实现.这样就可以通过抽象的方式提供一个核心的框架,其他具体的功能和业务都通过插件扩展的方式来 ...

随机推荐

  1. Thinking in UML 笔记(一) -- 面向对象

    一.UML 中最重要的就是面向对象. 面向对象的认识论可以构建更为复杂的系统来解释复杂的世界. 1. 面向过程,一切都是相互紧密地联系在一起,互相作用,互相影响. 2.面向对象, 世界是分割开的,只有 ...

  2. Windows进程间通讯(IPC)----套接字

    Windows套接字 Windows套接字即socket,通过socket可以实现在不同的进程间通信,甚至这两个进程可以不在同一个计算机中. Winsock使用步骤 服务端 socket初始化 创建套 ...

  3. sed -i 's/Search_String/Replacement_String/g' Input_File sed详细手册

    本文列出的十五个例子可以帮助你掌握 sed 命令.如果要使用 sed 命令删除文件中的行,去下面的文章.注意:由于这是一篇演示文章,我们使用不带 -i 选项的 sed 命令,该选项会在 Linux 终 ...

  4. 配置trunk和access

    配置trunk和access 拓扑图 PC地址设置 PC1 :192.168.1.1 vlan10 PC2 :192.168.1.2 vlan10 交换机配置 LSW3配置 <Huawei> ...

  5. Linux_WEB服务基础概念

    一.HTTPD简介 1️⃣:httpd是Apache超文本传输协议(HTTP) 服务器的主程序.被设计为一个独立运行的后台进程,它会建立一个处理请求的子进程或线程的池. 2️⃣:通常,httpd不应该 ...

  6. centos下yum方法安装apache+php+mysql

    yum(全称为:Yellow dog Updater,Modified) 是一个在Fedora和RedHat以及SUSE中的Shell前端管理软件.基于RPM包管理,能够从远处镜像服务器下载RPM包并 ...

  7. JS轮播图(网易云轮播图)

    JS 轮播图 写在前面 最聪明的人是最不愿浪费时间的人.--但丁 实现功能 图片自动切换 鼠标移入停止自动播放,显示按钮 点击按钮,实现前后翻 鼠标移入小圆圈,可以跳转到对应图片 点击左右两侧图片部分 ...

  8. system verilog内建数据类型

  9. 源码篇:Flutter Provider的另一面(万字图文+插件)

    前言 阅读此文的彦祖,亦菲们,附送一枚Provider模板代码生成插件! 我为啥要写这个插件呢? 此事说来话短,我这不准备写解析Provider源码的文章,肯定要写这框架的使用样例啊,然后再哔哔源码呀 ...

  10. 重新整理 .net core 实践篇—————服务与配置之间[十一二]

    前言 前面基本介绍了,官方对于asp .net core 设计配置和设计服务的框架的一些思路.看下服务和配置之间是如何联系的吧. 正文 服务: public interface ISelfServic ...