project.beforeEvaluate {
println “project.app.beforeEvaluate print”
}
println “project.app end”
如果是mac/linux,执行./gradlew 得到如下结果:
settings.gradle start
settings.gradle end
Configure project :
project.root start
project.root end
Configure project :app
project.app start
project.app end
project.app.afterEvaluate print
Groovy 语法
下面讲一些关于groovy的语法,可以打开Android Studio Tools-> Groovy Console练习Groovy 语法 ,如下
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bTKz39YQ-1638154551518)(https://user-gold-cdn.xitu.io/2019/9/17/16d3e0773023df7a?imageView2/0/w/1280/h/960/ignore-error/1)]
可选的类型定义,可以省略语句结束符分号(;)
int vs = 1
def version = ‘version1’
println(vs)
println(version)
括号也是可选的
println vs
println version
字符串定义
def s1 = ‘aaa’
def s2 = “version is ${version}”
def s3 = ‘’’ str
is
many
‘’’
println s1
println s2
println s3
集合
def list = [‘ant’,‘maven’]
list << “gradle”
list.add(‘test’)
println list.size()
println list.toString()
//map
def years = [‘key1’:1000,“key2”:2000]
println years.key1
println years.getClass()
输出结果
[ant, maven, gradle, test]
1000
class java.util.LinkedHashMap
闭包
groovy语法中支持闭包语法,闭包简单的说就是代码块,如下:
def v = {
v -> println v
}
static def testMethod(Closure closure){
closure(‘闭包 test’)
}
testMethod v
其中定义的v就为闭包,testMethod 为一个方法,传入参数为闭包,然后调用闭包.
解释 apply plugin: 'xxxx’和 dependencies{}
准备工作,看gradle的源码
我们先把子项目的build.gradle改为如下形式
apply plugin: ‘java-library’
repositories {
mavenLocal()
}
dependencies {
compile gradleApi()
}
这样,我们就可以直接看gradle的源码了,在External Libraries里如下
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cVVSSeeP-1638154551560)(https://user-gold-cdn.xitu.io/2019/9/17/16d3e2c252afef49?imageView2/0/w/1280/h/960/ignore-error/1)]
解释
进入build.gradle 点击apply 会进入到gradle的源码,可以看到
//PluginAware
/**
- Applies a plugin or script, using the given options provided as a map. Does nothing if the plugin has already been applied.
-
- The given map is applied as a series of method calls to a newly created {@link ObjectConfigurationAction}.
- That is, each key in the map is expected to be the name of a method {@link ObjectConfigurationAction} and the value to be compatible arguments to that method.
-
The following options are available:
-
- {@code from}: A script to apply. Accepts any path supported by {@link org.gradle.api.Project#uri(Object)}.
- {@code plugin}: The id or implementation class of the plugin to apply.
- {@code to}: The target delegate object or objects. The default is this plugin aware object. Use this to configure objects other than this object.
- @param options the options to use to configure and {@link ObjectConfigurationAction} before “executing” it
*/
void apply(Map<String, ?> options); 用Groovy 语法很清楚的解释,apply其实就是一个方法,后面传递的就是一个map,其中plugin为key.
那么dependencies{}也是一样
//Project
/**-
Configures the dependencies for this project.
-
This method executes the given closure against the {@link DependencyHandler} for this project. The {@link
- DependencyHandler} is passed to the closure as the closure’s delegate.
-
Examples:
- See docs for {@link DependencyHandler}
- @param configureClosure the closure to use to configure the dependencies.
*/
void dependencies(Closure configureClosure);
dependencies是一个方法 后面传递的是一个闭包的参数.
问题:思考那么android {}也是一样的实现吗? 后面讲解
Gradle Project/Task
《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整内容开源分享
=======
在前面章节中提到gralde初始化配置,是先解析并执行setting.gradle,然后在解析执行build.gradle,那么其实这些build.gradle 就是Project,外层的build.gradle是根Project,内层的为子project,根project只能有一个,子project可以有多个.
我们知道了最基础的gradle配置,那么怎么来使用Gradle里面的一些东西来为我们服务呢?
Plugin
前面提到apply plugin:‘xxxx’,这些plugin都是按照gradle规范来实现的,有java的有Android的,那么我们来实现一个自己的plugin.
把build.gradle 改为如下代码
//app build.gradle
class LibPlugin implements Plugin{
@Override
void apply(Project target) {
println ‘this is lib plugin’
}
}
apply plugin:LibPlugin运行./gradlew 结果如下
Configure project :app
this is lib pluginPlugin 之Extension
我们在自定义的Plugin中要获取Project的配置,可以通过Project去获取一些基本配置信息,那我们要自定义的一些属性怎么去配置获取呢,这时就需要创建Extension了,把上述代码改为如下形式。
//app build.gradle
class LibExtension{
String version
String message
}
class LibPlugin implements Plugin{
@Override
void apply(Project target) {
println ‘this is lib plugin’
//创建 Extension
target.extensions.create(‘libConfig’,LibExtension)
//创建一个task
target.tasks.create(‘libTask’,{
doLast{
LibExtension config = project.libConfig
println config.version
println config.message
}
})
}
}
apply plugin:LibPlugin
//配置
libConfig {
version = ‘1.0’
message = ‘lib message’
}配置完成后,执行./gradlew libTask 得到如下结果
Configure project :app
this is lib plugin
Task :lib:libTask
1.0
lib message看完上述代码,我们就知道android {} 其实他就是一个Extension, 他是由plugin ‘com.android.application’或者’com.android.library’ 创建。
Task
上述代码中,创建了一个名字为libTask的task,gradle中创建task的方式由很多中, 具体的创建接口在TaskContainer类中
//TaskContainer
Task create(Map<String, ?> options) throws InvalidUserDataException;
Task create(Map<String, ?> options, Closure configureClosure) throws InvalidUserDataException;
Task create(String name, Closure configureClosure) throws InvalidUserDataException;
Task create(String name) throws InvalidUserDataException;
T create(String name, Class type) throws InvalidUserDataException;
T create(String name, Class type, Action<? super T> configuration) throws InvalidUserDataException;Project不可以执行跑起来,那么我们就要定义一些task来完成我们的编译,运行,打包等。com.android.application插件 为我们定义了打包task如assemble,我们刚才定义的插件为我们添加了一个libTask用于输出。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mIGpDSA0-1638154551561)(https://user-gold-cdn.xitu.io/2019/9/17/16d3e77fc9cc60ff?imageView2/0/w/1280/h/960/ignore-error/1)]
Task API
我们看到创建的task里面可以直接调用doLast API,那是因为Task类中有doLast API,可以查看对应的代码看到对应的API
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-foPJeoIC-1638154551562)(https://user-gold-cdn.xitu.io/2019/9/17/16d3e818c3dbb8c5?imageView2/0/w/1280/h/960/ignore-error/1)]
Gradle的一些Task
gradle 为我们定义了一些常见的task,如clean,copy等,这些task可以直接使用name创建,如下:
task clean(type: Delete) {
delete rootProject.buildDir
}依赖task
我们知道Android打包时,会使用assemble相关的task,但是仅仅他是不能直接打包的,他会依赖其他的一些task. 那么怎么创建一个依赖的Task呢?代码如下
task A{
println “A task”
}
task B({
println ‘B task’
},dependsOn: A)执行./graldew B 输出
A task
B task自定义一个重命名APP名字的插件
通过上述的一些入门讲解,大概知道了gradle是怎么构建的,那现在来自定义一个安卓打包过程中,重命名APP名字的一个插件。
上述在build.gradle直接编写Plugin是OK的,那么为了复用性更高一些,那我们怎么把这个抽出去呢?
如下
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sl1uZ7g0-1638154551563)(https://user-gold-cdn.xitu.io/2019/9/17/16d3e91b752658ef?imageView2/0/w/1280/h/960/ignore-error/1)]
其中build.gradle为
apply plugin: ‘groovy’
apply plugin: ‘maven’
repositories {
mavenLocal()
jcenter()
}dependencies {
compile gradleApi()
}def versionName = “0.0.1”
group “com.ding.demo”
version versionName
uploadArchives{ //当前项目可以发布到本地文件夹中
repositories {
mavenDeployer {
repository(url: uri(’…/repo’)) //定义本地maven仓库的地址
}
}
}apkname.properties为
implementation-class=com.ding.demo.ApkChangeNamePlugin
ApkChangeNamePlugin
package com.ding.demo
import org.gradle.api.Project
import org.gradle.api.Pluginclass ApkChangeNamePlugin implements Plugin{
static class ChangeAppNameConfig{
String prefixName
String notConfig
}static def buildTime() {
return new Date().format(“yyyy_MM_dd_HH_mm_ss”, TimeZone.getTimeZone(“GMT+8”))
}@Override
void apply(Project project) {
if(!project.android){
throw new IllegalStateException(‘Must apply ‘com.android.application’ or ‘com.android.library’ first!’);
}
project.getExtensions().create(“nameConfig”,ChangeAppNameConfig)
ChangeAppNameConfig config
project.afterEvaluate {
config = project.nameConfig
}
project.android.applicationVariants.all{
variant ->
variant.outputs.all {
output ->
if (output.outputFile != null && output.outputFile.name.endsWith(’.apk’)
&& !output.outputFile.name.contains(config.notConfig)) {
def appName = config.prefixName
def time = buildTime()
String name = output.baseName
name = name.replaceAll("-", “_”)
outputFileName = “ a p p N a m e − {appName}- appName−{variant.versionCode}- n a m e − {name}- name−{time}.apk”
}
}
}
}
}定义完成后,执行./gradlew uploadArchives 会在本目录生成对应对应的插件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IjKipwkw-1638154551563)(https://user-gold-cdn.xitu.io/2019/9/17/16d3e94decfb19ed?imageView2/0/w/1280/h/960/ignore-error/1)]
应用插件 在根build.gralde 配置
buildscript {
repositories {
maven {url uri(’./repo/’)}
google()
jcenter()
}
dependencies {
classpath ‘com.android.tools.build:gradle:3.4.1’
classpath ‘com.ding.demo:apkname:0.0.1’
}
}在app.gralde 设置
apply plugin: ‘apkname’
nameConfig{
prefixName = ‘demo’
notConfig = ‘debug’
}Gradle doc 官网
Gradle的基础API差不多就介绍完了。
官网地址:docs.gradle.org/current/use…
可以去查看对应的API,也可以直接通过源码的方式查看但是笔记还没完,学习了Gradle的基础,我们要让其为我们服务。下面介绍几个实际应用.
APT 技术
www.jianshu.com/p/94aee6b02…
blog.csdn.net/kaifa1321/a…APT 全称Annotation Processing Tool,编译期解析注解,生成代码的一种技术。常用一些IOC框架的实现原理都是它,著名的ButterKnife,Dagger2就是用此技术实现的,SpringBoot中一些注入也是使用他进行注入的.
在介绍APT之前,先介绍一下SPI (Service Provider Interface)它通过在ClassPath路径下的META-INF/**文件夹查找文件,自动加载文件里所定义的类。 上面自定义的ApkNamePlugin 就是使用这种机制实现的,如下.
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g0FuKzVp-1638154551564)(https://user-gold-cdn.xitu.io/2019/9/18/16d4232c54745794?imageView2/0/w/1280/h/960/ignore-error/1)]
SPI 技术也有人用在了组件化的过程中进行解耦合。
要实现一个APT也是需要这种技术实现,但是谷歌已经把这个使用APT技术重新定义了一个,定义了一个auto-service,可以简化实现,下面就实现一个简单Utils的文档生成工具。
Utils文档生成插件
我们知道,项目中的Utils可能会很多,每当新人入职或者老员工也不能完成知道都有那些Utils了,可能会重复加入一些Utils,比如获取屏幕的密度,框高有很多Utils.我们通过一个小插件来生成一个文档,当用Utils可以看一眼文档就很一目了然了.
新建一个名为DocAnnotation的Java Libary
定义一个注解
@Retention(RetentionPolicy.CLASS)
public @interface GDoc {
String name() default “”;String author() default “”;
String time() default “”;
}新建一个名为DocComplie 的 Java Libary先
然后引入谷歌的 auto-service,引入DocAnnotation
apply plugin: ‘java-library’
dependencies {
implementation fileTree(dir: ‘libs’, include: [’*.jar’])
implementation ‘com.google.auto.service:auto-service:1.0-rc2’
implementation ‘com.alibaba:fastjson:1.2.34’
implementation project(’:DocAnnotation’)
}定义一个Entity类
public class Entity {
public String author;
public String time;
public String name;
}定义注解处理器
@AutoService(Processor.class) //其中这个注解就是 auto-service 提供的SPI功能
public class DocProcessor extends AbstractProcessor{Writer docWriter;
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);}
@Override
public Set getSupportedAnnotationTypes() {
//可处理的注解的集合
HashSet annotations = new HashSet<>();
String canonicalName = GDoc.class.getCanonicalName();
annotations.add(canonicalName);
return annotations;
}@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
Messager messager = processingEnv.getMessager();
Map<String,Entity> map = new HashMap<>();
StringBuilder stringBuilder = new StringBuilder();
for (Element e : env.getElementsAnnotatedWith(GDoc.class)) {
GDoc annotation = e.getAnnotation(GDoc.class);
Entity entity = new Entity();
entity.name = annotation.name();
entity.author = annotation.author();
entity.time = annotation.time();
map.put(e.getSimpleName().toString(),entity);@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
Messager messager = processingEnv.getMessager();
Map<String,Entity> map = new HashMap<>();
StringBuilder stringBuilder = new StringBuilder();
for (Element e : env.getElementsAnnotatedWith(GDoc.class)) {
GDoc annotation = e.getAnnotation(GDoc.class);
Entity entity = new Entity();
entity.name = annotation.name();
entity.author = annotation.author();
entity.time = annotation.time();
map.put(e.getSimpleName().toString(),entity);-