Gradle for Androider

本文参考:

gradle是用于构建项目的,在安卓项目中,gradle构建主要是针对.gradle文件,一个安卓项目中的.gradle文件有三种:

  • 根目录下的build.gradle:进行全局配置
  • 每一个子Moudle下的build.gradle:执行各个构建Task
  • settings.gradle:通过include函数将子Moudle的Project包含进来;

Groovy:

gradle是基于groovy语言的,groovy语言同java一样,会编译为字节码交给jvm执行,Groovy基于Java并拓展了Java,所以可以直接使用java中的规范,下面介绍一些groovy的扩展:

1. 变量定义:
 def variable1 = 1   //可以不使用分号结尾
 def varable2 = "I ama person"
 def  int x = 1  //变量定义时,也可以直接指定类型
2. 方法定义:
函数定义时,参数的类型也可以不指定。比如
String testFunction(arg1,arg2){ //无需指定参数类型
  ...
}
除了变量定义可以不指定类型外,Groovy中函数的返回值也可以是无类型的。
//无类型的函数定义,必须使用def关键字
def  nonReturnTypeFunc(){
    last_line   //最后一行代码的执行结果就是本函数的返回值
}
3. 闭包:

英文叫Closure,这是groovy中的一个数据类型,表示一段可以执行的代码,长这样:

def aClosure = {//闭包是一段代码,所以需要用花括号括起来..
    println"this is code" //这是代码,最后一句是返回值,
   //也可以使用return,和Groovy中普通函数一样
}
Closure闭包的注意点:
  • 闭包中有一个隐式参数it,类似java的this
  • 如果方法的最后一个参数式Closure类型,可以省略()

Gradle工作流程:

在了解Gradle的构建流程前,需要知道两个概念,Project,Task,每一个待编译的工程都叫做Project,每一个Project都包含了多个Task,比如Android APK的编译,可能包括Java源代码编译,资源编译,打包,签名;这些编译任务都是通过一个一个的Task完成的;

  1. Initialization:初始化阶段,Gradle 将会确定哪些项目将参与构建,并为每个项目创建一个 Project 对象实例。对于 Android 项目来说即为执行 setting.gradle 文件;

一般setting.gradle都是这样的:通过include函数将所有的Project包含进来

include ':app', ':a', ':b'
  1. Configuration:配置阶段,会解读每一个build.gradle文件,分析里面的task,然后将这些task形成一个有向图,表示task的依赖关系
task testBoth {
    println '我会在 Configuration 和 Execution 阶段都会执行'
    doFirst {
      println '我仅会在 testBoth 的 Execution 阶段执行'
    }
    doLast {
      println '我仅会在 testBoth 的 Execution 阶段执行'
    }
}

写在Task闭包的任务会在Configuration阶段运行,而doFirst,doLast会在Execution阶段运行;
切记大部分的内容是写在 doLast{} 或 doFirst{} 闭包中,因为写在如果写在 task 闭包中的话,会在 Configuration 阶段也被执行。

  1. Execution:task 的执行阶段。首先执行 doFirst {} 闭包中的内容,最后执行 doLast {} 闭包中的内容。
image

我们可以hook默认的构建过程,加入一些自定义的操作;

Project:

从上面我们可以知道每一个build.gradle都对应了一个Project对象,Project又包含了多个Task,每一个Task对应了一个构建任务;

@HasInternalProtocol
public interface Project extends Comparable, ExtensionAware, PluginAware {}

Project本质是一个Java类,可以通过Java提供的方法配置Project中的Task,因为每一个build.gradle都对应了一个具体的工程,所以需要为它指定一个插件,java工程添加java插件,安卓工程添加安卓插件:

apply plugin: 'com.android.library'
apply plugin: 'com.android.application'

使用的是apply函数,这个函数是PluginAware类下的函数

   /**
    * Applies a plugin or script, using the given options provided as a map. Does nothing if the plugin has already been applied.
    * <p>
    * 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.
    *
    * <p>The following options are available:</p>
    *
    * <ul><li>{@code from}: A script to apply. Accepts any path supported by {@link org.gradle.api.Project#uri(Object)}.</li>
    *
    * <li>{@code plugin}: The id or implementation class of the plugin to apply.</li>
    *
    * <li>{@code to}: The target delegate object or objects. The default is this plugin aware object. Use this to configure objects other than this object.</li></ul>
    *
    * @param options the options to use to configure and {@link ObjectConfigurationAction} before “executing” it
    */
   void apply(Map<String, ?> options);

Task:

task是gradle中的一种数据类型,它代表了需要执行的工作,可以使用task函数创建一个Task任务;

task cleanAuraBundleFile << {
    def providedDir = new File("$project.projectDir/auraBundles/provided-bundles")
    providedDir.deleteDir()
}

<<符号是doLast的缩写
[图片上传失败...(image-88ea71-1602754294365)]

.gradle文件中包含了多个 Script Block,让我们配置相关信息,不同的SB需要配置不同的东西:

根build.gradle:
buildscript {
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath "com.android.tools.build:gradle:4.0.1"

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

上面的buildscript,repository,dependencies,就是SB,他们分别配置不同的信息;他们可以在Gradle DSL 学习中找到每一个解释,比如allprojects是用于配置当前 project 和所有子 project 的,该方法将会在这些 project 中执行给定的闭包;

子build.gradle:
apply plugin: 'com.android.application'  // 引入插件

android {   // android的编译,增加了一种新类型的ScriptBlock-->android

    compileSdkVersion 30
    buildToolsVersion "30.0.2"

    defaultConfig {
        applicationId "com.example.myapplication"
        minSdkVersion 23
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    //implementation表示编译和运行时候需要的jar包,fileTree是一个函数
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

子项目的.gradle文件中的SB就可以在这里找到解释Android Plugin DSL 学习

自定义Gradle Plugin

1. 在Android项目下新建一个Android library:
2. 将Module里的内容删除,只保留build.gradle文件和src/main目录,同时移除build.gradle文件里的内容,将里面内容修改为:
apply plugin: 'groovy'
apply plugin: 'maven'

dependencies{
    // gradle sdk
    compile gradleApi()
    // groovy sdk
    compile localGroovy()
    compile 'com.android.tools.build:gradle:1.5.0'
}

repositories{
    mavenCentral()
}
3 将Module里的目录修改为以下格式:

在main目录下新建groovy目录,这时候groovy文件夹会被Android识别为groovy源码目录。除了在main目录下新建groovy目录外,你还要在main目录下新建resources目录,同理resources目录会被自动识别为资源文件夹。在groovy目录下新建项目包名,就像Java包名那样。resources目录下新建文件夹META-INF,META-INF文件夹下新建gradle-plugins文件夹。这样,就完成了gradle 插件的项目的整体搭建。

4 在groovy目录下创建groovy文件
import org.gradle.api.Plugin
import org.gradle.api.Project

public class PluginImpl implements Plugin<Project>{

    void apply(Project project){
        System.out.println("========================");
        System.out.println("hello gradle plugin!");
        System.out.println("========================");
    }
}
5 声明插件名称:

在resources/META-INF/gradle-plugins目录下新建一个properties文件,注意该文件的命名就是你使用插件的名字,这里命名为sayhello.properties,文件内容引入插件的类:

implementation-class=com.myplugin.PluginImpl

那么你在其他build.gradle文件中使用自定义的插件时候则需写成:

apply plugin: 'sayhello'
6 发布插件

在Model的build.gradle文件下插入一个发布的task

uploadArchives {
    repositories {
        mavenDeployer {
            pom.groupId = 'com.myplugins'
            pom.artifactId = 'myplugins'
            pom.version = 1.3
            // maven本地仓库的目录
            repository(url: uri('../packplugin'))
        }
    }
}

sync后在右侧的Task中心可以看到这个Task:



点击上传插件后就可以在项目中看到这个插件了:


7 在项目中引入插件

注意com后面的包名和插件名需要和upload的一致;

最后在app Model下引入:
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271