Android Gradle 学习笔记整理

括号也是可选的

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里如下

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

解释

进入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 plugin

    Plugin 之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.Plugin

    class 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;
    }
    }

    项目中引用


    《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
    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;
    }
    }

    项目中引用

    [外链图片转存中…(img-IJPrgqJn-1714544365495)]

    [外链图片转存中…(img-73E3W3Rw-1714544365496)]
    《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

  • 25
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值