其中定义的v就为闭包,testMethod 为一个方法,传入参数为闭包,然后调用闭包.
解释 apply plugin: 'xxxx’和 dependencies{}
准备工作,看gradle的源码
我们先把子项目的build.gradle改为如下形式
apply plugin: ‘java-library’
repositories {
mavenLocal()
}
dependencies {
compile gradleApi()
}
这样,我们就可以直接看gradle的源码了,在External Libraries里如下
解释
进入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
在前面章节中提到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用于输出。
Task API
我们看到创建的task里面可以直接调用doLast API,那是因为Task类中有doLast API,可以查看对应的代码看到对应的API
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的,那么为了复用性更高一些,那我们怎么把这个抽出去呢?
如下
其中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 会在本目录生成对应对应的插件
应用插件 在根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 就是使用这种机制实现的,如下.
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);stringBuilder.append(e.getSimpleName()).append(" “).append(entity.name).append(”\n");
}try {
docWriter = processingEnv.getFiler().createResource(
StandardLocation.SOURCE_OUTPUT,
“”,
“DescClassDoc.json”
).openWriter();//docWriter.append(JSON.toJSONString(map, SerializerFeature.PrettyFormat));
docWriter.append(stringBuilder.toString());
docWriter.flush();
docWriter.close();
} catch (IOException e) {
//e.printStackTrace();
//写入失败
}
return true;最后
分享一份NDK基础开发资料
分享内容包括不限于高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
写入失败
}
return true;最后
分享一份NDK基础开发资料
[外链图片转存中…(img-uO1gwpsy-1714278995340)]
分享内容包括不限于高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
-